From 8cd876df2c67fedab7f6b57cf8814369e884392d Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Tue, 15 Jul 2025 20:55:02 -0700 Subject: [PATCH 01/69] feat(lambda): Add Remote debugging support to Lambda Remote Invoke (#7678) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem * Enhanced Developer Experience - Current Remote Lambda debugging options are limited, primarily relying on print statements. LDK will significantly improve the developer experience by allowing real-time inspection of variables and execution flow. * Authentic Environment Debugging - Customers are eager to debug in the actual Lambda environment. However, Lambda doesn't allow direct SSH connections from external sources, making it challenging to access the runtime for debugging purposes. * Advantages over Local Debugging - While local debugging is a common alternative, Remote debugging offers significant benefits: Access to resources within VPCs, Ability to follow specific IAM rules and permissions which are typically not possible in a local debugging solution. * Efficient Problem Resolution - Debugging in the real Lambda environment provides the shortest path to identifying and resolving issues, as it eliminates [discrepancies](https://github.com/aws/aws-lambda-base-images/issues/112) between local and real Lambda environments. ## Solution Remote debugging (LDK) enable developers to remote debug live Lambda functions from AWS Toolkit for Visual Studio Code. LDK creates a secure tunnel between a developer’s local computer and a live Lambda function running in the cloud. With LDK, developers can use Visual Studio Code debugger to debug a running Lambda function, set break points, inspect variables, and step through the code. ### File structure - ldkClient.ts - Implement API calls to IoT & Lambda - localproxy.ts - Implement the local proxy to connect to the IoT SecureTunneling(ST) websocket and create a proxy tcp server - ldkController.ts - Control the debug workflow ### UI update ![image](https://github.com/user-attachments/assets/e526a46d-952c-45c5-aca6-77353d46ca7a) ### Arch ![image](https://github.com/user-attachments/assets/aaf90b74-3060-44b7-bde6-383e15b04910) ### Sequence Diagram ![image](https://github.com/user-attachments/assets/866b61dc-3872-41b0-a140-7b35d0c2a8be) --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- package-lock.json | 27 +- packages/core/package.json | 2 + packages/core/package.nls.json | 5 +- .../appBuilder/explorer/nodes/deployedNode.ts | 12 +- .../appBuilder/explorer/nodes/resourceNode.ts | 3 +- .../core/src/awsService/appBuilder/utils.ts | 17 +- packages/core/src/lambda/activation.ts | 5 +- .../src/lambda/commands/downloadLambda.ts | 66 +- .../src/lambda/explorer/lambdaFunctionNode.ts | 3 +- .../src/lambda/models/samLambdaRuntime.ts | 10 + .../src/lambda/remoteDebugging/ldkClient.ts | 470 +++++++++ .../lambda/remoteDebugging/ldkController.ts | 784 +++++++++++++++ .../src/lambda/remoteDebugging/ldkLayers.ts | 46 + .../src/lambda/remoteDebugging/localProxy.ts | 901 ++++++++++++++++++ .../core/src/lambda/remoteDebugging/utils.ts | 27 + .../lambda/vue/remoteInvoke/invokeLambda.ts | 558 ++++++++++- .../lambda/vue/remoteInvoke/remoteInvoke.css | 149 ++- .../lambda/vue/remoteInvoke/remoteInvoke.vue | 454 +++++++-- .../vue/remoteInvoke/remoteInvokeFrontend.ts | 363 ++++++- .../core/src/shared/clients/lambdaClient.ts | 129 ++- packages/core/src/shared/globalState.ts | 2 + .../src/shared/telemetry/vscodeTelemetry.json | 71 ++ .../core/src/shared/utilities/pathFind.ts | 39 + .../test/lambda/commands/deleteLambda.test.ts | 2 +- .../explorer/cloudFormationNodes.test.ts | 2 +- .../test/lambda/explorer/lambdaNodes.test.ts | 2 +- .../lambda/remoteDebugging/ldkClient.test.ts | 471 +++++++++ .../remoteDebugging/ldkController.test.ts | 600 ++++++++++++ .../lambda/remoteDebugging/localProxy.test.ts | 421 ++++++++ .../test/lambda/remoteDebugging/testUtils.ts | 177 ++++ .../vue/remoteInvoke/invokeLambda.test.ts | 3 +- .../invokeLambdaDebugging.test.ts | 595 ++++++++++++ .../explorer/nodes/deployedNode.test.ts | 4 + ...-ca5ca54f-d5f4-472e-934e-9fa79d783a98.json | 4 + packages/toolkit/package.json | 11 + 35 files changed, 6234 insertions(+), 201 deletions(-) create mode 100644 packages/core/src/lambda/remoteDebugging/ldkClient.ts create mode 100644 packages/core/src/lambda/remoteDebugging/ldkController.ts create mode 100644 packages/core/src/lambda/remoteDebugging/ldkLayers.ts create mode 100644 packages/core/src/lambda/remoteDebugging/localProxy.ts create mode 100644 packages/core/src/lambda/remoteDebugging/utils.ts create mode 100644 packages/core/src/test/lambda/remoteDebugging/ldkClient.test.ts create mode 100644 packages/core/src/test/lambda/remoteDebugging/ldkController.test.ts create mode 100644 packages/core/src/test/lambda/remoteDebugging/localProxy.test.ts create mode 100644 packages/core/src/test/lambda/remoteDebugging/testUtils.ts create mode 100644 packages/core/src/test/lambda/vue/remoteInvoke/invokeLambdaDebugging.test.ts create mode 100644 packages/toolkit/.changes/next-release/Feature-ca5ca54f-d5f4-472e-934e-9fa79d783a98.json diff --git a/package-lock.json b/package-lock.json index 3e625d1bd5b..da7a479fbb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16122,35 +16122,30 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.1", @@ -16161,35 +16156,30 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@sindresorhus/is": { @@ -24251,10 +24241,9 @@ "license": "MIT" }, "node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "dev": true, + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz", + "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==", "license": "Apache-2.0" }, "node_modules/lowercase-keys": { @@ -26025,10 +26014,9 @@ "license": "MIT" }, "node_modules/protobufjs": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.1.tgz", - "integrity": "sha512-3qx3IRjR9WPQKagdwrKjO3Gu8RgQR2qqw+1KnigWhoVjFqegIj1K3bP11sGqhxrO46/XL7lekuG4jmjL+4cLsw==", - "dev": true, + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { @@ -29629,7 +29617,6 @@ }, "node_modules/ws": { "version": "8.17.1", - "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -30053,6 +30040,7 @@ "mime-types": "^2.1.32", "node-fetch": "^2.7.0", "portfinder": "^1.0.32", + "protobufjs": "^7.2.6", "semver": "^7.5.4", "stream-buffers": "^3.0.2", "strip-ansi": "^5.2.0", @@ -30067,6 +30055,7 @@ "whatwg-url": "^14.0.0", "winston": "^3.11.0", "winston-transport": "^4.6.0", + "ws": "^8.16.0", "xml2js": "^0.6.1", "yaml-cfn": "^0.3.2" }, diff --git a/packages/core/package.json b/packages/core/package.json index baa8446aea9..6f8d27ef4dc 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -609,8 +609,10 @@ "whatwg-url": "^14.0.0", "winston": "^3.11.0", "winston-transport": "^4.6.0", + "ws": "^8.16.0", "xml2js": "^0.6.1", "yaml-cfn": "^0.3.2", + "protobufjs": "^7.2.6", "@svgdotjs/svg.js": "^3.0.16", "svgdom": "^0.1.0", "jaro-winkler": "^0.2.8" diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index c8c091a609e..20d500e07bd 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -99,7 +99,7 @@ "AWS.configuration.description.amazonq.workspaceIndexCacheDirPath": "The path to the directory that contains the cache of the index of your workspace files", "AWS.configuration.description.amazonq.ignoredSecurityIssues": "Specifies a list of code issue identifiers that Amazon Q should ignore when reviewing your workspace. Each item in the array should be a unique string identifier for a specific code issue. This allows you to suppress notifications for known issues that you've assessed and determined to be false positives or not applicable to your project. Use this setting with caution, as it may cause you to miss important security alerts.", "AWS.configuration.description.amazonq.proxy.certificateAuthority": "Path to a Certificate Authority (PEM file) for SSL/TLS verification when using a proxy.", - "AWS.command.apig.invokeRemoteRestApi": "Invoke in the cloud", + "AWS.command.apig.invokeRemoteRestApi": "Invoke remotely", "AWS.command.apig.invokeRemoteRestApi.cn": "Invoke on Amazon", "AWS.appBuilder.explorerTitle": "Application Builder", "AWS.appBuilder.explorerNode.noApps": "[This resource is not yet supported.]", @@ -159,11 +159,12 @@ "AWS.command.aboutToolkit": "About", "AWS.command.downloadLambda": "Download...", "AWS.command.uploadLambda": "Upload Lambda...", - "AWS.command.invokeLambda": "Invoke in the cloud", + "AWS.command.invokeLambda": "Invoke Remotely", "AWS.command.openLambdaFile": "Open your Lambda code", "AWS.command.quickDeployLambda": "Save and deploy your code", "AWS.command.openLambdaWorkspace": "Open in a workspace", "AWS.command.invokeLambda.cn": "Invoke on Amazon", + "AWS.command.remoteDebugging.clearSnapshot": "Reset Lambda Remote Debugging Snapshot", "AWS.command.lambda.convertToSam": "Convert to SAM Application", "AWS.command.refreshAwsExplorer": "Refresh Explorer", "AWS.command.refreshCdkExplorer": "Refresh CDK Explorer", diff --git a/packages/core/src/awsService/appBuilder/explorer/nodes/deployedNode.ts b/packages/core/src/awsService/appBuilder/explorer/nodes/deployedNode.ts index 470169a7725..100c6802c52 100644 --- a/packages/core/src/awsService/appBuilder/explorer/nodes/deployedNode.ts +++ b/packages/core/src/awsService/appBuilder/explorer/nodes/deployedNode.ts @@ -27,6 +27,7 @@ import { s3BucketType, } from '../../../../shared/cloudformation/cloudformation' import { ToolkitError } from '../../../../shared/errors' +import { ResourceTreeEntity } from '../samProject' const localize = nls.loadMessageBundle() export interface DeployedResource { @@ -77,7 +78,8 @@ export async function generateDeployedNode( deployedResource: any, regionCode: string, stackName: string, - resourceTreeEntity: any + resourceTreeEntity: ResourceTreeEntity, + location?: vscode.Uri ): Promise { let newDeployedResource: any const partitionId = globals.regionProvider.getPartitionId(regionCode) ?? defaultPartition @@ -90,7 +92,13 @@ export async function generateDeployedNode( try { configuration = (await defaultClient.getFunction(deployedResource.PhysicalResourceId)) .Configuration as Lambda.FunctionConfiguration - newDeployedResource = new LambdaFunctionNode(lambdaNode, regionCode, configuration) + newDeployedResource = new LambdaFunctionNode( + lambdaNode, + regionCode, + configuration, + undefined, + location ? vscode.Uri.joinPath(location, resourceTreeEntity.CodeUri ?? '').fsPath : undefined + ) } catch (error: any) { getLogger().error('Error getting Lambda configuration: %O', error) throw ToolkitError.chain(error, 'Error getting Lambda configuration', { diff --git a/packages/core/src/awsService/appBuilder/explorer/nodes/resourceNode.ts b/packages/core/src/awsService/appBuilder/explorer/nodes/resourceNode.ts index bda7b69ac4f..72de5afc60f 100644 --- a/packages/core/src/awsService/appBuilder/explorer/nodes/resourceNode.ts +++ b/packages/core/src/awsService/appBuilder/explorer/nodes/resourceNode.ts @@ -60,7 +60,8 @@ export class ResourceNode implements TreeNode { this.deployedResource, this.region, this.stackName, - this.resourceTreeEntity + this.resourceTreeEntity, + this.location.projectRoot ) } if (this.resourceTreeEntity.Type === SERVERLESS_FUNCTION_TYPE) { diff --git a/packages/core/src/awsService/appBuilder/utils.ts b/packages/core/src/awsService/appBuilder/utils.ts index ba5b3baf7f8..bdaa6293b30 100644 --- a/packages/core/src/awsService/appBuilder/utils.ts +++ b/packages/core/src/awsService/appBuilder/utils.ts @@ -569,19 +569,24 @@ export async function getLambdaHandlerFile( }) } + // if this function is used to get handler from a just downloaded lambda function zip. codeUri will be '' + if (codeUri !== '') { + folderUri = vscode.Uri.joinPath(folderUri, codeUri) + } + const handlerParts = handler.split('.') // sample: app.lambda_handler -> app.rb if (family === RuntimeFamily.Ruby) { // Ruby supports namespace/class handlers as well, but the path is // guaranteed to be slash-delimited so we can assume the first part is // the path - return vscode.Uri.joinPath(folderUri, codeUri, handlerParts.slice(0, handlerParts.length - 1).join('/') + '.rb') + return vscode.Uri.joinPath(folderUri, handlerParts.slice(0, handlerParts.length - 1).join('/') + '.rb') } // sample:app.lambda_handler -> app.py if (family === RuntimeFamily.Python) { // Otherwise (currently Node.js and Python) handle dot-delimited paths - return vscode.Uri.joinPath(folderUri, codeUri, handlerParts.slice(0, handlerParts.length - 1).join('/') + '.py') + return vscode.Uri.joinPath(folderUri, handlerParts.slice(0, handlerParts.length - 1).join('/') + '.py') } // sample: app.handler -> app.mjs/app.js @@ -591,8 +596,8 @@ export async function getLambdaHandlerFile( const handlerPath = path.dirname(handlerName) const handlerFile = path.basename(handlerName) const pattern = new vscode.RelativePattern( - vscode.Uri.joinPath(folderUri, codeUri, handlerPath), - `${handlerFile}.{js,mjs}` + vscode.Uri.joinPath(folderUri, handlerPath), + `${handlerFile}.{js,mjs,cjs,ts}` ) return searchHandlerFile(folderUri, pattern) } @@ -600,14 +605,14 @@ export async function getLambdaHandlerFile( // sample: ImageResize::ImageResize.Function::FunctionHandler -> Function.cs if (family === RuntimeFamily.DotNet) { const handlerName = path.basename(handler.split('::')[1].replaceAll('.', '/')) - const pattern = new vscode.RelativePattern(vscode.Uri.joinPath(folderUri, codeUri), `${handlerName}.cs`) + const pattern = new vscode.RelativePattern(folderUri, `${handlerName}.cs`) return searchHandlerFile(folderUri, pattern) } // sample: resizer.App::handleRequest -> App.java if (family === RuntimeFamily.Java) { const handlerName = handler.split('::')[0].replaceAll('.', '/') - const pattern = new vscode.RelativePattern(vscode.Uri.joinPath(folderUri, codeUri), `**/${handlerName}.java`) + const pattern = new vscode.RelativePattern(folderUri, `**/${handlerName}.java`) return searchHandlerFile(folderUri, pattern) } } diff --git a/packages/core/src/lambda/activation.ts b/packages/core/src/lambda/activation.ts index 9873371d40f..eaebc17de3b 100644 --- a/packages/core/src/lambda/activation.ts +++ b/packages/core/src/lambda/activation.ts @@ -13,7 +13,6 @@ import { uploadLambdaCommand } from './commands/uploadLambda' import { LambdaFunctionNode } from './explorer/lambdaFunctionNode' import { downloadLambdaCommand, openLambdaFile } from './commands/downloadLambda' import { tryRemoveFolder } from '../shared/filesystemUtilities' -import { ExtContext } from '../shared/extensions' import { invokeRemoteLambda } from './vue/remoteInvoke/invokeLambda' import { registerSamDebugInvokeVueCommand, registerSamInvokeVueCommand } from './vue/configEditor/samInvokeBackend' import { Commands } from '../shared/vscode/commands2' @@ -42,6 +41,8 @@ import { registerLambdaUriHandler } from './uriHandlers' import globals from '../shared/extensionGlobals' const localize = nls.loadMessageBundle() +import { activateRemoteDebugging } from './remoteDebugging/ldkController' +import { ExtContext } from '../shared/extensions' async function openReadme() { const readmeUri = vscode.Uri.file(await getReadme()) @@ -263,4 +264,6 @@ export async function activate(context: ExtContext): Promise { registerLambdaUriHandler() ) + + void activateRemoteDebugging() } diff --git a/packages/core/src/lambda/commands/downloadLambda.ts b/packages/core/src/lambda/commands/downloadLambda.ts index bc189b54c81..baf82b5b30c 100644 --- a/packages/core/src/lambda/commands/downloadLambda.ts +++ b/packages/core/src/lambda/commands/downloadLambda.ts @@ -27,9 +27,33 @@ import { telemetry } from '../../shared/telemetry/telemetry' import { Result, Runtime } from '../../shared/telemetry/telemetry' import { fs } from '../../shared/fs/fs' import { LambdaFunction } from './uploadLambda' +import globals from '../../shared/extensionGlobals' + +// Workspace state key for Lambda function ARN to local path cache +const LAMBDA_ARN_CACHE_KEY = 'aws.lambda.functionArnToLocalPathCache' // eslint-disable-line @typescript-eslint/naming-convention + +async function setLambdaArnCache(functionArn: string, localPath: string): Promise { + try { + const cache: Record = globals.context.workspaceState.get(LAMBDA_ARN_CACHE_KEY, {}) + cache[functionArn] = localPath + await globals.context.workspaceState.update(LAMBDA_ARN_CACHE_KEY, cache) + getLogger().debug(`lambda: cached local path for function ARN: ${functionArn} -> ${localPath}`) + } catch (error) { + getLogger().error(`lambda: failed to cache local path for function ARN: ${functionArn}`, error) + } +} + +export function getCachedLocalPath(functionArn: string): string | undefined { + const cache: Record = globals.context.workspaceState.get(LAMBDA_ARN_CACHE_KEY, {}) + return cache[functionArn] +} export async function downloadLambdaCommand(functionNode: LambdaFunctionNode) { const result = await runDownloadLambda(functionNode) + // check if result is Result + if (result instanceof vscode.Uri) { + return + } telemetry.lambda_import.emit({ result, @@ -37,7 +61,10 @@ export async function downloadLambdaCommand(functionNode: LambdaFunctionNode) { }) } -async function runDownloadLambda(functionNode: LambdaFunctionNode): Promise { +export async function runDownloadLambda( + functionNode: LambdaFunctionNode, + returnDir: boolean = false +): Promise { const workspaceFolders = vscode.workspace.workspaceFolders || [] const functionName = functionNode.configuration.FunctionName! @@ -74,6 +101,9 @@ async function runDownloadLambda(functionNode: LambdaFunctionNode): Promise { - return await vscode.window.withProgress( + selectedUri?: vscode.Uri, + returnDir: boolean = false +): Promise { + const result = await vscode.window.withProgress( { location: vscode.ProgressLocation.Notification, cancellable: false, @@ -107,8 +139,22 @@ export async function downloadLambdaInLocation( let lambdaLocation: string try { - lambdaLocation = path.join(downloadLocation, getLambdaDetails(lambda.configuration!).fileName) await downloadAndUnzipLambda(progress, lambda, downloadLocation) + // Cache the mapping of function ARN to downloaded location + if (lambda.configuration?.FunctionArn) { + await setLambdaArnCache(lambda.configuration.FunctionArn, downloadLocation) + } + lambdaLocation = path.join(downloadLocation, getLambdaDetails(lambda.configuration!).fileName) + if (!(await fs.exists(lambdaLocation))) { + // if file ext is mjs, change to js or vice versa + const currentExt = path.extname(lambdaLocation) + const alternativeExt = currentExt === '.mjs' ? '.js' : '.mjs' + const alternativePath = lambdaLocation.replace(currentExt, alternativeExt) + + if (await fs.exists(alternativePath)) { + lambdaLocation = alternativePath + } + } } catch (e) { // initial download failed or runtime is unsupported. // show error and return a failure @@ -127,6 +173,7 @@ export async function downloadLambdaInLocation( } try { + await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup') await openLambdaFile(lambdaLocation) if (workspaceFolders) { if ( @@ -148,6 +195,12 @@ export async function downloadLambdaInLocation( } } ) + + if (returnDir) { + return vscode.Uri.file(downloadLocation) + } else { + return result + } } async function downloadAndUnzipLambda( @@ -205,6 +258,7 @@ export async function openLambdaFile(lambdaLocation: string, viewColumn?: vscode void vscode.window.showWarningMessage(warning) throw new Error() } + await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup') const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(lambdaLocation)) await vscode.window.showTextDocument(doc, viewColumn) } diff --git a/packages/core/src/lambda/explorer/lambdaFunctionNode.ts b/packages/core/src/lambda/explorer/lambdaFunctionNode.ts index 2093a9585d4..03cb9210aaa 100644 --- a/packages/core/src/lambda/explorer/lambdaFunctionNode.ts +++ b/packages/core/src/lambda/explorer/lambdaFunctionNode.ts @@ -27,7 +27,8 @@ export class LambdaFunctionNode extends AWSTreeNodeBase implements AWSResourceNo public readonly parent: AWSTreeNodeBase, public override readonly regionCode: string, public configuration: Lambda.FunctionConfiguration, - public override readonly contextValue?: string + public override readonly contextValue?: string, + public localDir?: string ) { super( `${configuration.FunctionArn}`, diff --git a/packages/core/src/lambda/models/samLambdaRuntime.ts b/packages/core/src/lambda/models/samLambdaRuntime.ts index d6d8683e28b..06e35dbcd2b 100644 --- a/packages/core/src/lambda/models/samLambdaRuntime.ts +++ b/packages/core/src/lambda/models/samLambdaRuntime.ts @@ -99,6 +99,16 @@ const defaultRuntimes = ImmutableMap([ [RuntimeFamily.Ruby, 'ruby3.3'], ]) +export const mapFamilyToDebugType = ImmutableMap([ + [RuntimeFamily.NodeJS, 'node'], + [RuntimeFamily.Python, 'python'], + [RuntimeFamily.DotNet, 'csharp'], + [RuntimeFamily.Go, 'go'], + [RuntimeFamily.Java, 'java'], + [RuntimeFamily.Ruby, 'ruby'], + [RuntimeFamily.Unknown, 'unknown'], +]) + export const samZipLambdaRuntimes: ImmutableSet = ImmutableSet.union([ nodeJsRuntimes, pythonRuntimes, diff --git a/packages/core/src/lambda/remoteDebugging/ldkClient.ts b/packages/core/src/lambda/remoteDebugging/ldkClient.ts new file mode 100644 index 00000000000..915e150b039 --- /dev/null +++ b/packages/core/src/lambda/remoteDebugging/ldkClient.ts @@ -0,0 +1,470 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { IoTSecureTunneling, Lambda } from 'aws-sdk' +import { getClientId } from '../../shared/telemetry/util' +import { DefaultLambdaClient } from '../../shared/clients/lambdaClient' +import { LocalProxy } from './localProxy' +import globals from '../../shared/extensionGlobals' +import { getLogger } from '../../shared/logger/logger' +import { getIoTSTClientWithAgent, getLambdaClientWithAgent } from './utils' +import { ToolkitError } from '../../shared/errors' +import * as nls from 'vscode-nls' + +const localize = nls.loadMessageBundle() + +export function isTunnelInfo(data: TunnelInfo): data is TunnelInfo { + return ( + typeof data === 'object' && + data !== null && + typeof data.tunnelID === 'string' && + typeof data.sourceToken === 'string' && + typeof data.destinationToken === 'string' + ) +} + +interface TunnelInfo { + tunnelID: string + sourceToken: string + destinationToken: string +} + +async function callUpdateFunctionConfiguration( + lambda: DefaultLambdaClient, + config: Lambda.FunctionConfiguration, + waitForUpdate: boolean +): Promise { + // Update function configuration back to original values + return await lambda.updateFunctionConfiguration( + { + FunctionName: config.FunctionName!, + Timeout: config.Timeout, + Layers: config.Layers?.map((layer) => layer.Arn!).filter(Boolean) || [], + Environment: { + Variables: config.Environment?.Variables ?? {}, + }, + }, + { + maxRetries: 5, + initialDelayMs: 2000, + backoffMultiplier: 2, + waitForUpdate: waitForUpdate, + } + ) +} + +export class LdkClient { + static #instance: LdkClient + private localProxy: LocalProxy | undefined + private static instanceCreating = false + private lambdaClientCache: Map = new Map() + private iotSTClientCache: Map = new Map() + + constructor() {} + + public static get instance() { + if (this.#instance !== undefined) { + return this.#instance + } + if (this.instanceCreating) { + getLogger().warn( + localize( + 'AWS.lambda.ldkClient.multipleInstancesError', + 'Attempt to create multiple LdkClient instances simultaneously' + ) + ) + } + // Set flag to prevent recursive instance creation + this.instanceCreating = true + try { + const self = (this.#instance = new this()) + return self + } finally { + this.instanceCreating = false + } + } + + /** + * Get or create a cached Lambda client for the specified region + */ + private getLambdaClient(region: string): DefaultLambdaClient { + if (!this.lambdaClientCache.has(region)) { + this.lambdaClientCache.set(region, getLambdaClientWithAgent(region)) + } + return this.lambdaClientCache.get(region)! + } + + private async getIoTSTClient(region: string): Promise { + if (!this.iotSTClientCache.has(region)) { + this.iotSTClientCache.set(region, await getIoTSTClientWithAgent(region)) + } + return this.iotSTClientCache.get(region)! + } + /** + * Clean up all resources held by this client + * Should be called when the extension is deactivated + */ + public dispose(): void { + if (this.localProxy) { + this.localProxy.stop() + this.localProxy = undefined + } + // Clear the Lambda client cache + this.iotSTClientCache.clear() + this.lambdaClientCache.clear() + } + + // Create or reuse tunnel + async createOrReuseTunnel(region: string): Promise { + try { + // Get VSCode UUID using getClientId from telemetry.utils.ts + const vscodeUuid = getClientId(globals.globalState) + + // Create IoTSecureTunneling client + const iotSecureTunneling = await this.getIoTSTClient(region) + + // Define tunnel identifier + const tunnelIdentifier = `RemoteDebugging+${vscodeUuid}` + const timeoutInMinutes = 720 + // List existing tunnels + const listTunnelsResponse = await iotSecureTunneling.listTunnels({}).promise() + + // Find tunnel with our identifier + const existingTunnel = listTunnelsResponse.tunnelSummaries?.find( + (tunnel) => tunnel.description === tunnelIdentifier && tunnel.status?.toLowerCase() === 'open' + ) + + if (existingTunnel && existingTunnel.tunnelId) { + const timeCreated = existingTunnel?.createdAt ? new Date(existingTunnel.createdAt) : new Date() + const expiryTime = new Date(timeCreated.getTime() + timeoutInMinutes * 60 * 1000) + const currentTime = new Date() + const minutesRemaining = (expiryTime.getTime() - currentTime.getTime()) / (60 * 1000) + + if (minutesRemaining >= 15) { + // Rotate access tokens for the existing tunnel + const rotateResponse = await this.refreshTunnelTokens(existingTunnel.tunnelId, region) + + return rotateResponse + } else { + // Close tunnel if less than 15 minutes remaining + await iotSecureTunneling + .closeTunnel({ + tunnelId: existingTunnel.tunnelId, + delete: false, + }) + .promise() + + getLogger().info(`Closed tunnel ${existingTunnel.tunnelId} with less than 15 minutes remaining`) + } + } + + // Create new tunnel + const openTunnelResponse = await iotSecureTunneling + .openTunnel({ + description: tunnelIdentifier, + timeoutConfig: { + maxLifetimeTimeoutMinutes: timeoutInMinutes, // 12 hours + }, + destinationConfig: { + services: ['WSS'], + }, + }) + .promise() + + getLogger().info(`Created new tunnel with ID: ${openTunnelResponse.tunnelId}`) + + return { + tunnelID: openTunnelResponse.tunnelId || '', + sourceToken: openTunnelResponse.sourceAccessToken || '', + destinationToken: openTunnelResponse.destinationAccessToken || '', + } + } catch (error) { + throw ToolkitError.chain(error, 'Error creating/reusing tunnel') + } + } + + // Refresh tunnel tokens + async refreshTunnelTokens(tunnelId: string, region: string): Promise { + try { + const iotSecureTunneling = await this.getIoTSTClient(region) + const rotateResponse = await iotSecureTunneling + .rotateTunnelAccessToken({ + tunnelId: tunnelId, + clientMode: 'ALL', + }) + .promise() + + return { + tunnelID: tunnelId, + sourceToken: rotateResponse.sourceAccessToken || '', + destinationToken: rotateResponse.destinationAccessToken || '', + } + } catch (error) { + throw ToolkitError.chain(error, 'Error refreshing tunnel tokens') + } + } + + async getFunctionDetail(functionArn: string): Promise { + try { + const region = getRegionFromArn(functionArn) + if (!region) { + getLogger().error( + localize( + 'AWS.lambda.ldkClient.couldNotDetermineRegion', + 'Could not determine region from Lambda ARN' + ) + ) + return undefined + } + const client = this.getLambdaClient(region) + const configuration = (await client.getFunction(functionArn)).Configuration as Lambda.FunctionConfiguration + // get function detail + // return function detail + return configuration + } catch (error) { + getLogger().warn(`Error getting function detail:${error}`) + return undefined + } + } + + // Create debug deployment to given lambda function + // save a snapshot of the current config to global : aws.lambda.remoteDebugContext + // we are 1: changing function timeout to 15 minute + // 2: adding the ldk layer LDK_LAYER_ARN_X86_64 or LDK_LAYER_ARN_ARM64 (ignore if already added, fail if 5 layer already there) + // 3: adding two param to lambda environment variable + // {AWS_LAMBDA_EXEC_WRAPPER:/opt/bin/ldk_wrapper, AWS_LDK_DESTINATION_TOKEN: destinationToken } + async createDebugDeployment( + config: Lambda.FunctionConfiguration, + destinationToken: string, + lambdaTimeout: number, + shouldPublishVersion: boolean, + ldkLayerArn: string, + progress: vscode.Progress<{ message?: string | undefined; increment?: number | undefined }> + ): Promise { + try { + if (!config.FunctionArn || !config.FunctionName) { + throw new Error(localize('AWS.lambda.ldkClient.functionArnMissing', 'Function ARN is missing')) + } + const region = getRegionFromArn(config.FunctionArn ?? '') + if (!region) { + throw new Error( + localize( + 'AWS.lambda.ldkClient.couldNotDetermineRegion', + 'Could not determine region from Lambda ARN' + ) + ) + } + + // fix out of bound timeout + if (lambdaTimeout && (lambdaTimeout > 900 || lambdaTimeout <= 0)) { + lambdaTimeout = 900 + } + + // Inform user about the changes that will be made + + progress.report({ message: localize('AWS.lambda.ldkClient.applyingChanges', 'Applying changes...') }) + + // Determine architecture and select appropriate layer + + const layers = config.Layers || [] + + // Check if LDK layer is already added + const ldkLayerExists = layers.some( + (layer) => layer.Arn?.includes('LDKLayerX86') || layer.Arn?.includes('LDKLayerArm64') + ) + + // Check if we have room to add a layer (max 5) + if (!ldkLayerExists && layers.length >= 5) { + throw new Error( + localize( + 'AWS.lambda.ldkClient.cannotAddLdkLayer', + 'Cannot add LDK layer: Lambda function already has 5 layers' + ) + ) + } + // Create updated layers list + const updatedLayers = ldkLayerExists + ? layers.map((layer) => layer.Arn!).filter(Boolean) + : [...layers.map((layer) => layer.Arn!).filter(Boolean), ldkLayerArn] + + // Create updated environment variables + const currentEnv = config.Environment?.Variables || {} + const updatedEnv: { [key: string]: string } = { + ...currentEnv, + AWS_LAMBDA_EXEC_WRAPPER: '/opt/bin/ldk_wrapper', + AWS_LAMBDA_DEBUG_ON_LATEST: shouldPublishVersion ? 'false' : 'true', + AWS_LDK_DESTINATION_TOKEN: destinationToken, + } + if (currentEnv['AWS_LAMBDA_EXEC_WRAPPER']) { + updatedEnv.ORIGINAL_AWS_LAMBDA_EXEC_WRAPPER = currentEnv['AWS_LAMBDA_EXEC_WRAPPER'] + } + + // Create Lambda client using AWS SDK + const lambda = this.getLambdaClient(region) + + // Update function configuration + if (!config.FunctionArn || !config.FunctionName) { + throw new Error('Function ARN is missing') + } + + // Create a temporary config for the update + const updateConfig: Lambda.FunctionConfiguration = { + FunctionName: config.FunctionName, + Timeout: lambdaTimeout ?? 900, // 15 minutes + Layers: updatedLayers.map((arn) => ({ Arn: arn })), + Environment: { + Variables: updatedEnv, + }, + } + + await callUpdateFunctionConfiguration(lambda, updateConfig, true) + + // publish version + let version = '$Latest' + if (shouldPublishVersion) { + // should somehow return version for debugging + const versionResp = await lambda.publishVersion(config.FunctionName, { waitForUpdate: true }) + version = versionResp.Version ?? '' + // remove debug deployment in a non-blocking way + void Promise.resolve( + callUpdateFunctionConfiguration(lambda, config, false).then(() => { + progress.report({ + message: localize( + 'AWS.lambda.ldkClient.debugDeploymentCompleted', + 'Debug deployment completed successfully' + ), + }) + }) + ) + } + return version + } catch (error) { + getLogger().error(`Error creating debug deployment: ${error}`) + if (error instanceof Error) { + throw new ToolkitError(`Failed to create debug deployment: ${error.message}`) + } + return 'Failed' + } + } + + // Remove debug deployment from the given lambda function + // use the snapshot we took before create debug deployment + // we are 1: reverting timeout to it's original snapshot + // 2: reverting layer status according to it's original snapshot + // 3: reverting environment back to it's original snapshot + async removeDebugDeployment(config: Lambda.FunctionConfiguration, check: boolean = true): Promise { + try { + if (!config.FunctionArn || !config.FunctionName) { + throw new Error('Function ARN is missing') + } + const region = getRegionFromArn(config.FunctionArn ?? '') + if (!region) { + throw new Error('Could not determine region from Lambda ARN') + } + + if (check) { + const currentConfig = await this.getFunctionDetail(config.FunctionArn) + if ( + currentConfig?.Timeout === config?.Timeout && + currentConfig?.Layers?.length === config?.Layers?.length + ) { + // nothing to remove + return true + } + } + + // Create Lambda client using AWS SDK + const lambda = this.getLambdaClient(region) + + // Update function configuration back to original values + await callUpdateFunctionConfiguration(lambda, config, false) + + return true + } catch (error) { + // no need to raise, even this failed we want the following to execute + throw ToolkitError.chain(error, 'Error removing debug deployment') + } + } + + async deleteDebugVersion(functionArn: string, qualifier: string) { + try { + const region = getRegionFromArn(functionArn) + if (!region) { + throw new Error('Could not determine region from Lambda ARN') + } + const lambda = this.getLambdaClient(region) + await lambda.deleteFunction(functionArn, qualifier) + return true + } catch (error) { + getLogger().error('Error deleting debug version: %O', error) + return false + } + } + + // Start proxy with better resource management + async startProxy(region: string, sourceToken: string, port: number = 0): Promise { + try { + getLogger().info(`Starting direct proxy for region:${region}`) + + // Clean up any existing proxy thoroughly + if (this.localProxy) { + getLogger().info('Stopping existing proxy before starting a new one') + this.localProxy.stop() + this.localProxy = undefined + + // Small delay to ensure resources are released + await new Promise((resolve) => setTimeout(resolve, 100)) + } + + // Create and start a new local proxy + this.localProxy = new LocalProxy() + + // Start the proxy and get the assigned port + const localPort = await this.localProxy.start(region, sourceToken, port) + getLogger().info(`Local proxy started successfully on port ${localPort}`) + return true + } catch (error) { + getLogger().error(`Failed to start proxy: ${error}`) + if (this.localProxy) { + this.localProxy.stop() + this.localProxy = undefined + } + throw ToolkitError.chain(error, 'Failed to start proxy') + } + } + + // Stop proxy with proper cleanup and reference handling + async stopProxy(): Promise { + try { + getLogger().info(`Stopping proxy`) + + if (this.localProxy) { + // Ensure proper resource cleanup + this.localProxy.stop() + + // Force delete the reference to allow GC + this.localProxy = undefined + + getLogger().info('Local proxy stopped successfully') + } else { + getLogger().info('No active local proxy to stop') + } + + return true + } catch (error) { + throw ToolkitError.chain(error, 'Error stopping proxy') + } + } +} + +// Helper function to extract region from ARN +export function getRegionFromArn(arn: string | undefined): string | undefined { + if (!arn) { + return undefined + } + const parts = arn.split(':') + return parts.length >= 4 ? parts[3] : undefined +} diff --git a/packages/core/src/lambda/remoteDebugging/ldkController.ts b/packages/core/src/lambda/remoteDebugging/ldkController.ts new file mode 100644 index 00000000000..c4c08b10254 --- /dev/null +++ b/packages/core/src/lambda/remoteDebugging/ldkController.ts @@ -0,0 +1,784 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import { getLogger } from '../../shared/logger/logger' +import globals from '../../shared/extensionGlobals' +import { Lambda } from 'aws-sdk' +import { getRegionFromArn, isTunnelInfo, LdkClient } from './ldkClient' +import { getFamily, mapFamilyToDebugType } from '../models/samLambdaRuntime' +import { findJavaPath } from '../../shared/utilities/pathFind' +import { ToolkitError } from '../../shared/errors' +import { showConfirmationMessage, showMessage } from '../../shared/utilities/messages' +import { telemetry } from '../../shared/telemetry/telemetry' +import * as nls from 'vscode-nls' +import { getRemoteDebugLayer } from './ldkLayers' +import path from 'path' +import { glob } from 'glob' +import { Commands } from '../../shared/vscode/commands2' + +const localize = nls.loadMessageBundle() +const logger = getLogger() +export const remoteDebugContextString = 'aws.lambda.remoteDebugContext' +export const remoteDebugSnapshotString = 'aws.lambda.remoteDebugSnapshot' + +// Map debug types to their corresponding VS Code extension IDs +const mapDebugTypeToExtensionId = new Map([ + ['python', ['ms-python.python']], + ['java', ['redhat.java', 'vscjava.vscode-java-debug']], + ['node', ['ms-vscode.js-debug']], +]) + +const mapExtensionToBackup = new Map([['ms-vscode.js-debug', 'ms-vscode.js-debug-nightly']]) + +export interface DebugConfig { + functionArn: string + functionName: string + port: number + localRoot: string + remoteRoot: string + skipFiles: string[] + shouldPublishVersion: boolean + lambdaRuntime?: string // Lambda runtime (e.g., nodejs18.x) + debuggerRuntime?: string // VS Code debugger runtime (e.g., node) + outFiles?: string[] + sourceMap?: boolean + justMyCode?: boolean + projectName?: string + otherDebugParams?: string + lambdaTimeout?: number + layerArn?: string + handlerFile?: string +} + +// Helper function to create a human-readable diff message +function createDiffMessage( + config: Lambda.FunctionConfiguration, + currentConfig: Lambda.FunctionConfiguration, + isRevert: boolean = true +): string { + let message = isRevert ? 'The following changes will be reverted:\n\n' : 'The following changes will be made:\n\n' + + message += + '1. Timeout: ' + + (currentConfig.Timeout || 'default') + + ' seconds → ' + + (config.Timeout || 'default') + + ' seconds\n' + + message += '2. Layers: ' + const hasLdkLayer = currentConfig.Layers?.some( + (layer) => layer.Arn?.includes('LDKLayerX86') || layer.Arn?.includes('LDKLayerArm64') + ) + + message += hasLdkLayer ? 'Remove LDK layer\n' : 'No Change\n' + + message += '3. Environment Variables: Remove AWS_LAMBDA_EXEC_WRAPPER and AWS_LDK_DESTINATION_TOKEN\n' + + return message +} + +/** + * Attempts to revert an existing debug configuration if one exists + * @returns true if revert was successful or no config exists, false if revert failed or user chose not to revert + */ +export async function revertExistingConfig(): Promise { + try { + // Check if a debug context exists from a previous session + const savedConfig = getLambdaSnapshot() + + if (!savedConfig) { + // No existing config to revert + return true + } + + // clear the snapshot for it's corrupted + if (!savedConfig.FunctionArn || !savedConfig.FunctionName) { + logger.error('Function ARN or Function Name is missing, cannot revert') + void (await persistLambdaSnapshot(undefined)) + return true + } + + // compare with current config + const currentConfig = await LdkClient.instance.getFunctionDetail(savedConfig.FunctionArn) + // could be permission issues, or user has deleted previous function, we should remove the snapshot + if (!currentConfig) { + logger.error('Failed to get current function state, cannot revert') + void (await persistLambdaSnapshot(undefined)) + return true + } + + if ( + currentConfig?.Timeout === savedConfig?.Timeout && + currentConfig?.Layers?.length === savedConfig?.Layers?.length + ) { + // No changes needed, remove the snapshot + void (await persistLambdaSnapshot(undefined)) + return true + } + + // Create a diff message to show what will be changed + const diffMessage = currentConfig + ? createDiffMessage(savedConfig, currentConfig, true) + : 'Failed to get current function state' + + const response = await showConfirmationMessage({ + prompt: localize( + 'AWS.lambda.remoteDebug.revertPreviousDeployment', + 'A previous debug deployment was detected for {0}. Would you like to revert those changes before proceeding?\n\n{1}', + savedConfig.FunctionName, + diffMessage + ), + confirm: localize('AWS.lambda.remoteDebug.revert', 'Revert'), + cancel: localize('AWS.lambda.remoteDebug.dontShowAgain', "Don't show again"), + type: 'warning', + }) + + if (!response) { + // User chose not to revert, remove the snapshot + void (await persistLambdaSnapshot(undefined)) + return true + } + + await LdkClient.instance.removeDebugDeployment(savedConfig, false) + await persistLambdaSnapshot(undefined) + void showMessage( + 'info', + localize( + 'AWS.lambda.remoteDebug.successfullyReverted', + 'Successfully reverted changes to {0}', + savedConfig.FunctionName + ) + ) + + return true + } catch (error) { + throw ToolkitError.chain(error, `Error in revertExistingConfig`) + } +} + +export async function activateRemoteDebugging(): Promise { + try { + globals.context.subscriptions.push( + Commands.register('aws.lambda.remoteDebugging.clearSnapshot', async () => { + void (await persistLambdaSnapshot(undefined)) + }) + ) + } catch (error) { + logger.error(`Error in registering clearSnapshot command:${error}`) + } + + try { + logger.info('Remote debugging is initiated') + + // Use the revertExistingConfig function to handle any existing debug configurations + await revertExistingConfig() + + // Initialize RemoteDebugController to ensure proper startup state + RemoteDebugController.instance.ensureCleanState() + } catch (error) { + // show warning + void vscode.window.showWarningMessage(`Error in activateRemoteDebugging: ${error}`) + logger.error(`Error in activateRemoteDebugging:${error}`) + } +} + +// this should be called when the debug session is started +async function persistLambdaSnapshot(config: Lambda.FunctionConfiguration | undefined): Promise { + try { + await globals.globalState.update(remoteDebugSnapshotString, config) + } catch (error) { + // TODO raise toolkit error + logger.error(`Error persisting debug sessions:${error}`) + } +} + +export function getLambdaSnapshot(): Lambda.FunctionConfiguration | undefined { + return globals.globalState.get(remoteDebugSnapshotString) +} + +/** + * Helper function to check if a string is a valid VSCode glob pattern + */ +function isVscodeGlob(pattern: string): boolean { + // Check for common glob patterns: *, **, ?, [], {} + return /[*?[\]{}]/.test(pattern) +} + +/** + * Helper function to validate source map files exist for given outFiles patterns + */ +async function validateSourceMapFiles(outFiles: string[]): Promise { + const allAreGlobs = outFiles.every((pattern) => isVscodeGlob(pattern)) + if (!allAreGlobs) { + return false + } + + try { + let jsfileCount = 0 + let mapfileCount = 0 + const jsFiles = await glob(outFiles, { ignore: 'node_modules/**' }) + + for (const file of jsFiles) { + if (file.includes('js')) { + jsfileCount += 1 + } + if (file.includes('.map')) { + mapfileCount += 1 + } + } + + return jsfileCount === 0 || mapfileCount === 0 ? false : true + } catch (error) { + getLogger().warn(`Error validating source map files: ${error}`) + return false + } +} + +function processOutFiles(outFiles: string[], localRoot: string): string[] { + const processedOutFiles: string[] = [] + + for (let outFile of outFiles) { + if (!outFile.includes('*')) { + // add * in the end + outFile = path.join(outFile, '*') + } + if (!path.isAbsolute(outFile)) { + // Find which workspace contains the localRoot path + const workspaceFolders = vscode.workspace.workspaceFolders + if (workspaceFolders) { + let matchingWorkspace: vscode.WorkspaceFolder | undefined + + // Check if localRoot is within any workspace + for (const workspace of workspaceFolders) { + const absoluteLocalRoot = path.resolve(localRoot) + const workspacePath = workspace.uri.fsPath + + if (absoluteLocalRoot.startsWith(workspacePath)) { + matchingWorkspace = workspace + break + } + } + + if (matchingWorkspace) { + // Join workspace folder with the relative outFile path + processedOutFiles.push(path.join(matchingWorkspace.uri.fsPath, outFile)) + } else { + // If no matching workspace found, use the original outFile + processedOutFiles.push(outFile) + } + } else { + // No workspace folders, use the original outFile + processedOutFiles.push(outFile) + } + } else { + // Already absolute path, use as is + processedOutFiles.push(outFile) + } + } + return processedOutFiles +} + +async function getVscodeDebugConfig( + functionConfig: Lambda.FunctionConfiguration, + debugConfig: DebugConfig +): Promise { + // Parse and validate otherDebugParams if provided + let additionalParams: Record = {} + if (debugConfig.otherDebugParams) { + try { + const parsed = JSON.parse(debugConfig.otherDebugParams) + if (typeof parsed === 'object' && !Array.isArray(parsed)) { + additionalParams = parsed + getLogger().info('Additional debug parameters parsed successfully: %O ', additionalParams) + } else { + void vscode.window.showWarningMessage( + localize( + 'AWS.lambda.remoteDebug.invalidDebugParams', + 'Other Debug Parameters must be a valid JSON object. The parameter will be ignored.' + ) + ) + getLogger().warn(`Invalid otherDebugParams format: expected object, got ${typeof parsed}`) + } + } catch (error) { + void vscode.window.showWarningMessage( + localize( + 'AWS.lambda.remoteDebug.failedToParseDebugParams', + 'Failed to parse Other Debug Parameters as JSON: {0}. The parameter will be ignored.', + error instanceof Error ? error.message : 'Invalid JSON' + ) + ) + getLogger().warn(`Failed to parse otherDebugParams as JSON: ${error}`) + } + } + + const debugSessionName = `Debug ${functionConfig.FunctionArn!.split(':').pop()}` + + // Define debugConfig before the try block + const debugType = mapFamilyToDebugType.get(getFamily(functionConfig.Runtime ?? ''), 'unknown') + let vsCodeDebugConfig: vscode.DebugConfiguration + switch (debugType) { + case 'node': + // source map support + if (debugConfig.sourceMap && debugConfig.outFiles) { + // process outFiles first, if they are relative path (not starting with /), + // check local root path is located in which workspace. Then join workspace Folder with outFiles + + // Update debugConfig with processed outFiles + debugConfig.outFiles = processOutFiles(debugConfig.outFiles, debugConfig.localRoot) + + // Use glob to search if there are any matching js file or source map file + const hasSourceMaps = await validateSourceMapFiles(debugConfig.outFiles) + + if (hasSourceMaps) { + // support mapping common sam cli location + additionalParams['sourceMapPathOverrides'] = { + ...additionalParams['sourceMapPathOverrides'], + '?:*/T/?:*/*': path.join(debugConfig.localRoot, '*'), + } + debugConfig.localRoot = debugConfig.outFiles[0].split('*')[0] + } else { + debugConfig.sourceMap = false + debugConfig.outFiles = undefined + await showMessage( + 'warn', + localize( + 'AWS.lambda.remoteDebug.outFileNotFound', + 'outFiles not valid or no js and map file found in outFiles, debug will continue without sourceMap support' + ) + ) + } + } + vsCodeDebugConfig = { + type: debugType, + request: 'attach', + name: debugSessionName, + address: 'localhost', + port: debugConfig.port, + localRoot: debugConfig.localRoot, + remoteRoot: debugConfig.remoteRoot, + skipFiles: debugConfig.skipFiles, + sourceMaps: debugConfig.sourceMap, + outFiles: debugConfig.outFiles, + continueOnAttach: debugConfig.outFiles ? false : true, + stopOnEntry: false, + timeout: 60000, + ...additionalParams, // Merge additional debug parameters + } + break + case 'python': + vsCodeDebugConfig = { + type: debugType, + request: 'attach', + name: debugSessionName, + port: debugConfig.port, + cwd: debugConfig.localRoot, + pathMappings: [ + { + localRoot: debugConfig.localRoot, + remoteRoot: debugConfig.remoteRoot, + }, + ], + justMyCode: debugConfig.justMyCode ?? true, + ...additionalParams, // Merge additional debug parameters + } + break + case 'java': + vsCodeDebugConfig = { + type: debugType, + request: 'attach', + name: debugSessionName, + hostName: 'localhost', + port: debugConfig.port, + sourcePaths: [debugConfig.localRoot], + projectName: debugConfig.projectName, + timeout: 60000, + ...additionalParams, // Merge additional debug parameters + } + break + default: + throw new ToolkitError(`Unsupported debug type: ${debugType}`) + } + getLogger().info('VS Code debug configuration: %O', vsCodeDebugConfig) + return vsCodeDebugConfig +} + +export class RemoteDebugController { + static #instance: RemoteDebugController + isDebugging: boolean = false + qualifier: string | undefined = undefined + private lastDebugStartTime: number = 0 + // private debugSession: DebugSession | undefined + private debugSessionDisposables: Map = new Map() + + public static get instance() { + if (this.#instance !== undefined) { + return this.#instance + } + + const self = (this.#instance = new this()) + return self + } + + constructor() {} + + /** + * Ensures the controller is in a clean state at startup or before a new operation + */ + public ensureCleanState(): void { + this.isDebugging = false + this.qualifier = undefined + + // Clean up any leftover disposables + for (const [key, disposable] of this.debugSessionDisposables.entries()) { + try { + disposable.dispose() + } catch (e) { + // Ignore errors during startup cleanup + } + this.debugSessionDisposables.delete(key) + } + } + + public supportCodeDownload(runtime: string | undefined): boolean { + if (!runtime) { + return false + } + try { + return ['node', 'python'].includes(mapFamilyToDebugType.get(getFamily(runtime)) ?? '') + } catch { + // deprecated runtime + return false + } + } + + public supportRuntimeRemoteDebug(runtime: string | undefined): boolean { + if (!runtime) { + return false + } + try { + return ['node', 'python', 'java'].includes(mapFamilyToDebugType.get(getFamily(runtime)) ?? '') + } catch { + return false + } + } + + public getRemoteDebugLayer( + region: string | undefined, + architectures: Lambda.ArchitecturesList | undefined + ): string | undefined { + if (!region || !architectures) { + return undefined + } + if (architectures.includes('x86_64')) { + return getRemoteDebugLayer(region, 'x86_64') + } + if (architectures.includes('arm64')) { + return getRemoteDebugLayer(region, 'arm64') + } + return undefined + } + + public async installDebugExtension(runtime: string | undefined): Promise { + if (!runtime) { + throw new ToolkitError('Runtime is undefined') + } + + const debugType = mapFamilyToDebugType.get(getFamily(runtime)) + if (!debugType) { + throw new ToolkitError(`Debug type is undefined for runtime ${runtime}`) + } + // Install needed debug extension based on runtime + const extensions = mapDebugTypeToExtensionId.get(debugType) + if (extensions) { + for (const extension of extensions) { + const extensionObj = vscode.extensions.getExtension(extension) + const backupExtensionObj = vscode.extensions.getExtension(mapExtensionToBackup.get(extension) ?? '') + + if (!extensionObj && !backupExtensionObj) { + // Extension is not installed, install it + const choice = await showConfirmationMessage({ + prompt: localize( + 'AWS.lambda.remoteDebug.extensionNotInstalled', + 'You need to install the {0} extension to debug {1} functions. Would you like to install it now?', + extension, + debugType + ), + confirm: localize('AWS.lambda.remoteDebug.install', 'Install'), + cancel: localize('AWS.lambda.remoteDebug.cancel', 'Cancel'), + type: 'warning', + }) + if (!choice) { + return false + } + await vscode.commands.executeCommand('workbench.extensions.installExtension', extension) + if (vscode.extensions.getExtension(extension) === undefined) { + return false + } + } + } + } + + if (debugType === 'java' && !(await findJavaPath())) { + // jvm not available + const choice = await showConfirmationMessage({ + prompt: localize( + 'AWS.lambda.remoteDebug.jvmNotInstalled', + 'You need to install a JVM to debug Java functions. Would you like to install it now?' + ), + confirm: localize('AWS.lambda.remoteDebug.install', 'Install'), + cancel: localize('AWS.lambda.remoteDebug.continueAnyway', 'Continue Anyway'), + type: 'warning', + }) + // open https://developers.redhat.com/products/openjdk/download + if (choice) { + await vscode.env.openExternal( + vscode.Uri.parse('https://developers.redhat.com/products/openjdk/download') + ) + return false + } + } + // passed all checks + return true + } + + public async startDebugging(functionArn: string, runtime: string, debugConfig: DebugConfig): Promise { + if (this.isDebugging) { + getLogger().error('Debug already in progress, remove debug setup to restart') + return + } + + await telemetry.lambda_remoteDebugStart.run(async (span) => { + // Create a copy of debugConfig without functionName and functionArn for telemetry + const debugConfigForTelemetry: Partial = { ...debugConfig } + debugConfigForTelemetry.functionName = undefined + debugConfigForTelemetry.functionArn = undefined + debugConfigForTelemetry.localRoot = undefined + + span.record({ + source: 'remoteDebug', + passive: false, + action: JSON.stringify(debugConfigForTelemetry), + }) + this.lastDebugStartTime = Date.now() + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: 'Setting up debug session', + cancellable: false, + }, + async (progress) => { + // Reset state before starting + this.ensureCleanState() + + getLogger().info(`Starting debugger for ${functionArn}`) + + const region = getRegionFromArn(functionArn) + if (!region) { + throw new ToolkitError('Could not determine region from Lambda ARN') + } + + // Check if runtime / region is supported for remote debugging + if (!this.supportRuntimeRemoteDebug(runtime)) { + throw new ToolkitError( + `Runtime ${runtime} is not supported for remote debugging. ` + + `Only Python, Node.js, and Java runtimes are supported.` + ) + } + + // Check if a snapshot already exists and revert if needed + // Use the revertExistingConfig function from ldkController + progress.report({ message: 'Checking if snapshot exists...' }) + const revertResult = await revertExistingConfig() + + // If revert failed and user didn't choose to ignore, abort the deployment + if (revertResult === false) { + return + } + try { + // Anything fails before this point doesn't requires reverting + this.isDebugging = true + + // the following will contain changes that requires reverting. + // Create a snapshot of lambda config before debug + // let's preserve this config to a global variable at here + // we will use this config to revert the changes back to it once was, once confirm it's success, update the global to undefined + // if somehow the changes failed to revert, in init phase(activate remote debugging), we will detect this config and prompt user to revert the changes + const ldkClient = LdkClient.instance + // get function config again in case anything changed + const functionConfig = await LdkClient.instance.getFunctionDetail(functionArn) + if (!functionConfig?.Runtime || !functionConfig?.FunctionArn) { + throw new ToolkitError('Could not retrieve Lambda function configuration') + } + await persistLambdaSnapshot(functionConfig) + + // Record runtime in telemetry + span.record({ + runtimeString: functionConfig.Runtime as any, + }) + + // Create or reuse tunnel + progress.report({ message: 'Creating secure tunnel...' }) + getLogger().info('Creating secure tunnel...') + const tunnelInfo = await ldkClient.createOrReuseTunnel(region) + if (!tunnelInfo) { + throw new ToolkitError(`Empty tunnel info response, please retry:${tunnelInfo}`) + } + + if (!isTunnelInfo(tunnelInfo)) { + throw new ToolkitError(`Invalid tunnel info response:${tunnelInfo}`) + } + // start update lambda funcion, await in the end + // Create debug deployment + progress.report({ message: 'Configuring Lambda function for debugging...' }) + getLogger().info('Configuring Lambda function for debugging...') + + const layerArn = + debugConfig.layerArn ?? this.getRemoteDebugLayer(region, functionConfig.Architectures) + if (!layerArn) { + throw new ToolkitError(`No Layer Arn is provided`) + } + // start this request and await in the end + const debugDeployPromise = ldkClient.createDebugDeployment( + functionConfig, + tunnelInfo.destinationToken, + debugConfig.lambdaTimeout ?? 900, + debugConfig.shouldPublishVersion, + layerArn, + progress + ) + + const vscodeDebugConfig = await getVscodeDebugConfig(functionConfig, debugConfig) + // show every field in debugConfig + // getLogger().info(`Debug configuration created successfully ${JSON.stringify(debugConfig)}`) + + // Start local proxy with timeout and better error handling + progress.report({ message: 'Starting local proxy...' }) + + const proxyStartTimeout = new Promise((_, reject) => { + setTimeout(() => reject(new Error('Local proxy start timed out')), 30000) + }) + + const proxyStartAttempt = ldkClient.startProxy(region, tunnelInfo.sourceToken, debugConfig.port) + + const proxyStarted = await Promise.race([proxyStartAttempt, proxyStartTimeout]) + + if (!proxyStarted) { + throw new ToolkitError('Failed to start local proxy') + } + getLogger().info('Local proxy started successfully') + progress.report({ message: 'Starting debugger...' }) + // Start debugging in a non-blocking way + void Promise.resolve(vscode.debug.startDebugging(undefined, vscodeDebugConfig)).then( + async (debugStarted) => { + if (!debugStarted) { + // this could be triggered by another stop debugging, let's check state before stopping. + throw new ToolkitError('Failed to start debug session') + } + } + ) + + const debugSessionEndDisposable = vscode.debug.onDidTerminateDebugSession(async (session) => { + if (session.name === vscodeDebugConfig.name) { + void (await this.stopDebugging()) + } + }) + + // wait until lambda function update is completed + progress.report({ message: 'Waiting for function update...' }) + const qualifier = await debugDeployPromise + if (!qualifier || qualifier === 'Failed') { + throw new ToolkitError('Failed to configure Lambda function for debugging') + } + // store the published version for debugging in version + if (debugConfig.shouldPublishVersion) { + // we already reverted + this.qualifier = qualifier + } + + // Store the disposable + this.debugSessionDisposables.set(functionConfig.FunctionArn, debugSessionEndDisposable) + progress.report({ + message: `Debug session setup completed for ${functionConfig.FunctionArn.split(':').pop()}`, + }) + } catch (error) { + try { + await this.stopDebugging() + } catch (errStop) { + getLogger().error( + 'encountered following error when stoping debug for failed debug session:' + ) + getLogger().error(errStop as Error) + } + + throw ToolkitError.chain(error, 'Error StartDebugging') + } + } + ) + }) + } + + public async stopDebugging(): Promise { + await telemetry.lambda_remoteDebugStop.run(async (span) => { + if (!this.isDebugging) { + void showMessage( + 'info', + localize('AWS.lambda.remoteDebug.debugNotInProgress', 'Debug is not in progress') + ) + return + } + span.record({ duration: this.lastDebugStartTime === 0 ? 0 : Date.now() - this.lastDebugStartTime }) + try { + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: 'Stopping debug session', + cancellable: false, + }, + async (progress) => { + progress.report({ message: 'Stopping debugging...' }) + const ldkClient = LdkClient.instance + + // First attempt to clean up resources from Lambda + const savedConfig = getLambdaSnapshot() + if (!savedConfig?.FunctionArn) { + getLogger().error('No saved configuration found during cleanup') + throw new ToolkitError('No saved configuration found during cleanup') + } + + const disposable = this.debugSessionDisposables.get(savedConfig.FunctionArn) + if (disposable) { + disposable.dispose() + this.debugSessionDisposables.delete(savedConfig.FunctionArn) + } + getLogger().info(`Removing debug deployment for function: ${savedConfig.FunctionName}`) + + await vscode.commands.executeCommand('workbench.action.debug.stop') + // Then stop the proxy (with more reliable error handling) + getLogger().info('Stopping proxy during cleanup') + await ldkClient.stopProxy() + // Ensure our resources are properly cleaned up + if (this.qualifier) { + await ldkClient.deleteDebugVersion(savedConfig.FunctionArn, this.qualifier) + } + if (await ldkClient.removeDebugDeployment(savedConfig, true)) { + await persistLambdaSnapshot(undefined) + } + + progress.report({ message: `Debug session stopped` }) + } + ) + void showMessage( + 'info', + localize('AWS.lambda.remoteDebug.debugSessionStopped', 'Debug session stopped') + ) + } catch (error) { + throw ToolkitError.chain(error, 'error when stopping remote debug') + } finally { + this.isDebugging = false + } + }) + } +} diff --git a/packages/core/src/lambda/remoteDebugging/ldkLayers.ts b/packages/core/src/lambda/remoteDebugging/ldkLayers.ts new file mode 100644 index 00000000000..5573a84f980 --- /dev/null +++ b/packages/core/src/lambda/remoteDebugging/ldkLayers.ts @@ -0,0 +1,46 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +interface RegionAccountMapping { + [region: string]: string +} + +// Map region to account ID +export const regionToAccount: RegionAccountMapping = { + 'us-east-1': '166855510987', + 'ap-northeast-1': '435951944084', + 'us-west-1': '397974708477', + 'us-west-2': '116489046076', + 'us-east-2': '372632330791', + 'ca-central-1': '816313119386', + 'eu-west-1': '020236748984', + 'eu-west-2': '199003954714', + 'eu-west-3': '490913546906', + 'eu-central-1': '944487268028', + 'eu-north-1': '351516301086', + 'ap-southeast-1': '812073016575', + 'ap-southeast-2': '185226997092', + 'ap-northeast-2': '241511115815', + 'ap-south-1': '926022987530', + 'sa-east-1': '313162186107', + 'ap-east-1': '416298298123', + 'me-south-1': '511027370648', + 'me-central-1': '766358817862', +} + +// Global layer version +const globalLayerVersion = 1 + +export function getRemoteDebugLayer(region: string, arch: string): string | undefined { + const account = regionToAccount[region] + + if (!account) { + return undefined + } + + const layerName = arch === 'x86_64' ? 'LDKLayerX86' : 'LDKLayerArm64' + + return `arn:aws:lambda:${region}:${account}:layer:${layerName}:${globalLayerVersion}` +} diff --git a/packages/core/src/lambda/remoteDebugging/localProxy.ts b/packages/core/src/lambda/remoteDebugging/localProxy.ts new file mode 100644 index 00000000000..8b228deeb1a --- /dev/null +++ b/packages/core/src/lambda/remoteDebugging/localProxy.ts @@ -0,0 +1,901 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as net from 'net' +import WebSocket from 'ws' +import * as crypto from 'crypto' +import { getLogger } from '../../shared/logger/logger' +import { v4 as uuidv4 } from 'uuid' +import * as protobuf from 'protobufjs' + +const logger = getLogger() + +// Define the message types from the protocol +enum MessageType { + UNKNOWN = 0, + DATA = 1, + STREAM_START = 2, + STREAM_RESET = 3, + SESSION_RESET = 4, + SERVICE_IDS = 5, + CONNECTION_START = 6, + CONNECTION_RESET = 7, +} + +// Interface for tunnel info +export interface TunnelInfo { + tunnelId: string + sourceToken: string + destinationToken: string +} + +// Interface for TCP connection +interface TcpConnection { + socket: net.Socket + streamId: number + connectionId: number +} + +/** + * LocalProxy class that handles WebSocket connection to IoT secure tunneling + * and sets up a TCP adapter as a local proxy + */ +export class LocalProxy { + private ws: WebSocket.WebSocket | undefined = undefined + private tcpServer: net.Server | undefined = undefined + private tcpConnections: Map = new Map() + private isConnected: boolean = false + private reconnectAttempts: number = 0 + private maxReconnectAttempts: number = 10 + private reconnectInterval: number = 2500 // 2.5 seconds + private pingInterval: NodeJS.Timeout | undefined = undefined + private serviceId: string = 'WSS' + private currentStreamId: number = 1 + private nextConnectionId: number = 1 + private localPort: number = 0 + private region: string = '' + private accessToken: string = '' + private Message: protobuf.Type | undefined = undefined + private clientToken: string = '' + private eventHandlers: { [key: string]: any[] } = {} + private isDisposed: boolean = false + + constructor() { + void this.loadProtobufDefinition() + } + + // Define the protobuf schema as a string constant + private static readonly protobufSchema = ` + syntax = "proto3"; + + package com.amazonaws.iot.securedtunneling; + + message Message { + Type type = 1; + int32 streamId = 2; + bool ignorable = 3; + bytes payload = 4; + string serviceId = 5; + repeated string availableServiceIds = 6; + uint32 connectionId = 7; + + enum Type { + UNKNOWN = 0; + DATA = 1; + STREAM_START = 2; + STREAM_RESET = 3; + SESSION_RESET = 4; + SERVICE_IDS = 5; + CONNECTION_START = 6; + CONNECTION_RESET = 7; + } + }` + + /** + * Load the protobuf definition from the embedded schema string + */ + private async loadProtobufDefinition(): Promise { + try { + if (this.Message) { + // Already loaded, don't parse again + return + } + + const root = protobuf.parse(LocalProxy.protobufSchema).root + this.Message = root.lookupType('com.amazonaws.iot.securedtunneling.Message') + + if (!this.Message) { + throw new Error('Failed to load Message type from protobuf definition') + } + + logger.debug('Protobuf definition loaded successfully') + } catch (error) { + logger.error(`Error loading protobuf definition:${error}`) + throw error + } + } + + /** + * Start the local proxy + * @param region AWS region + * @param sourceToken Source token for the tunnel + * @param port Local port to listen on + */ + public async start(region: string, sourceToken: string, port: number = 0): Promise { + // Reset disposal state when starting + this.isDisposed = false + + this.region = region + this.accessToken = sourceToken + + try { + // Start TCP server first + this.localPort = await this.startTcpServer(port) + + // Then connect to WebSocket + await this.connectWebSocket() + + return this.localPort + } catch (error) { + logger.error(`Failed to start local proxy:${error}`) + this.stop() + throw error + } + } + + /** + * Stop the local proxy and clean up all resources + */ + public stop(): void { + if (this.isDisposed) { + logger.debug('LocalProxy already stopped, skipping duplicate stop call') + return + } + + logger.debug('Stopping LocalProxy and cleaning up resources') + + // Cancel any pending reconnect timeouts + if (this.eventHandlers['reconnectTimeouts']) { + for (const timeoutId of this.eventHandlers['reconnectTimeouts']) { + clearTimeout(timeoutId as NodeJS.Timeout) + } + } + + this.stopPingInterval() + this.closeWebSocket() + this.closeTcpServer() + + // Reset all state + this.clientToken = '' + this.isConnected = false + this.reconnectAttempts = 0 + this.currentStreamId = 1 + this.nextConnectionId = 1 + this.localPort = 0 + this.region = '' + this.accessToken = '' + + // Mark as disposed to prevent duplicate stop calls + this.isDisposed = true + + // Clear any remaining event handlers reference + this.eventHandlers = {} + } + + /** + * Start the TCP server + * @param port Port to listen on (0 for random port) + * @returns The port the server is listening on + */ + private startTcpServer(port: number): Promise { + return new Promise((resolve, reject) => { + try { + this.tcpServer = net.createServer((socket) => { + this.handleNewTcpConnection(socket) + }) + + this.tcpServer.on('error', (err) => { + logger.error(`TCP server error:${err}`) + }) + + this.tcpServer.listen(port, '127.0.0.1', () => { + const address = this.tcpServer?.address() as net.AddressInfo + this.localPort = address.port + logger.debug(`TCP server listening on port ${this.localPort}`) + resolve(this.localPort) + }) + } catch (error) { + logger.error(`Failed to start TCP server:${error}`) + reject(error) + } + }) + } + + /** + * Close the TCP server and all connections + */ + private closeTcpServer(): void { + if (this.tcpServer) { + logger.debug('Closing TCP server and connections') + + // Remove all listeners from the server + this.tcpServer.removeAllListeners('error') + this.tcpServer.removeAllListeners('connection') + this.tcpServer.removeAllListeners('listening') + + // Close all TCP connections with proper error handling + for (const connection of this.tcpConnections.values()) { + try { + // Remove all listeners before destroying + connection.socket.removeAllListeners('data') + connection.socket.removeAllListeners('error') + connection.socket.removeAllListeners('close') + connection.socket.destroy() + } catch (err) { + logger.error(`Error closing TCP connection: ${err}`) + } + } + this.tcpConnections.clear() + + // Close the server with proper error handling and timeout + try { + // Set a timeout in case server.close() hangs + const serverCloseTimeout = setTimeout(() => { + logger.warn('TCP server close timed out, forcing closure') + this.tcpServer = undefined + }, 5000) + + this.tcpServer.close(() => { + clearTimeout(serverCloseTimeout) + logger.debug('TCP server closed successfully') + this.tcpServer = undefined + }) + } catch (err) { + logger.error(`Error closing TCP server: ${err}`) + this.tcpServer = undefined + } + } + } + + /** + * Handle a new TCP connection with proper resource management + * @param socket The TCP socket + */ + private handleNewTcpConnection(socket: net.Socket): void { + if (!this.isConnected || this.isDisposed) { + logger.warn('WebSocket not connected or proxy disposed, rejecting TCP connection') + socket.destroy() + return + } + + const connectionId = this.nextConnectionId++ + const streamId = this.currentStreamId + + logger.debug(`New TCP connection: ${connectionId}`) + + // Track event handlers for this connection + const handlers: { [event: string]: (...args: any[]) => void } = {} + + // Data handler + const dataHandler = (data: Buffer) => { + this.sendData(streamId, connectionId, data) + } + socket.on('data', dataHandler) + handlers.data = dataHandler + + // Error handler + const errorHandler = (err: Error) => { + logger.error(`TCP connection ${connectionId} error: ${err}`) + this.sendConnectionReset(streamId, connectionId) + + // Cleanup handlers on error + this.cleanupSocketHandlers(socket, handlers) + } + socket.on('error', errorHandler) + handlers.error = errorHandler + + // Close handler + const closeHandler = () => { + logger.debug(`TCP connection ${connectionId} closed`) + + // Remove from connections map and send reset + this.tcpConnections.delete(connectionId) + this.sendConnectionReset(streamId, connectionId) + + // Cleanup handlers on close + this.cleanupSocketHandlers(socket, handlers) + } + socket.on('close', closeHandler) + handlers.close = closeHandler + + // Set a timeout to close idle connections after 10 minutes + const idleTimeout = setTimeout( + () => { + if (this.tcpConnections.has(connectionId)) { + logger.debug(`Closing idle TCP connection ${connectionId}`) + socket.destroy() + } + }, + 10 * 60 * 1000 + ) + + // Clear timeout on socket close + socket.once('close', () => { + clearTimeout(idleTimeout) + }) + + // Store the connection + const connection: TcpConnection = { + socket, + streamId, + connectionId, + } + this.tcpConnections.set(connectionId, connection) + + // Send StreamStart for the first connection, ConnectionStart for subsequent ones + if (connectionId === 1) { + this.sendStreamStart(streamId, connectionId) + } else { + this.sendConnectionStart(streamId, connectionId) + } + } + + /** + * Helper method to clean up socket event handlers + * @param socket The socket to clean up + * @param handlers The handlers to remove + */ + private cleanupSocketHandlers(socket: net.Socket, handlers: { [event: string]: (...args: any[]) => void }): void { + try { + if (handlers.data) { + socket.removeListener('data', handlers.data as (...args: any[]) => void) + } + if (handlers.error) { + socket.removeListener('error', handlers.error as (...args: any[]) => void) + } + if (handlers.close) { + socket.removeListener('close', handlers.close as (...args: any[]) => void) + } + } catch (error) { + logger.error(`Error cleaning up socket handlers: ${error}`) + } + } + + /** + * Connect to the WebSocket server with proper event tracking + */ + private async connectWebSocket(): Promise { + if (this.ws) { + this.closeWebSocket() + } + + // Reset for new connection + this.isDisposed = false + + return new Promise((resolve, reject) => { + try { + const url = `wss://data.tunneling.iot.${this.region}.amazonaws.com:443/tunnel?local-proxy-mode=source` + + if (!this.clientToken) { + this.clientToken = uuidv4().replace(/-/g, '') + } + + this.ws = new WebSocket.WebSocket(url, ['aws.iot.securetunneling-3.0'], { + headers: { + 'access-token': this.accessToken, + 'client-token': this.clientToken, + }, + handshakeTimeout: 30000, // 30 seconds + }) + + // Track event listeners for proper cleanup + this.eventHandlers['wsOpen'] = [] + this.eventHandlers['wsMessage'] = [] + this.eventHandlers['wsClose'] = [] + this.eventHandlers['wsError'] = [] + this.eventHandlers['wsPing'] = [] + this.eventHandlers['wsPong'] = [] + + // Open handler + const openHandler = () => { + logger.debug('WebSocket connected') + this.isConnected = true + this.reconnectAttempts = 0 + this.startPingInterval() + resolve() + } + this.ws.on('open', openHandler) + this.eventHandlers['wsOpen'].push(openHandler) + + // Message handler + const messageHandler = (data: WebSocket.RawData) => { + this.handleWebSocketMessage(data) + } + this.ws.on('message', messageHandler) + this.eventHandlers['wsMessage'].push(messageHandler) + + // Close handler + const closeHandler = (code: number, reason: Buffer) => { + logger.debug(`WebSocket closed: ${code} ${reason.toString()}`) + this.isConnected = false + this.stopPingInterval() + + // Only attempt reconnect if we haven't explicitly stopped + if (!this.isDisposed) { + void this.attemptReconnect() + } + } + this.ws.on('close', closeHandler) + this.eventHandlers['wsClose'].push(closeHandler) + + // Error handler + const errorHandler = (err: Error) => { + logger.error(`WebSocket error: ${err}`) + reject(err) + } + this.ws.on('error', errorHandler) + this.eventHandlers['wsError'].push(errorHandler) + + // Ping handler + const pingHandler = (data: Buffer) => { + // Respond to ping with pong + if (this.ws && this.ws.readyState === WebSocket.OPEN) { + this.ws.pong(data) + } + } + this.ws.on('ping', pingHandler) + this.eventHandlers['wsPing'].push(pingHandler) + + // Pong handler + const pongHandler = () => { + logger.debug('Received pong') + } + this.ws.on('pong', pongHandler) + this.eventHandlers['wsPong'].push(pongHandler) + + // Set connection timeout + const connectionTimeout = setTimeout(() => { + if (this.ws && this.ws.readyState !== WebSocket.OPEN) { + logger.error('WebSocket connection timed out') + this.closeWebSocket() + reject(new Error('WebSocket connection timed out')) + } + }, 35000) // 35 seconds (slightly longer than handshake timeout) + + // Add a handler to clear the timeout on successful connection + this.ws.once('open', () => { + clearTimeout(connectionTimeout) + }) + } catch (error) { + logger.error(`Failed to connect WebSocket: ${error}`) + this.isConnected = false + reject(error) + } + }) + } + + /** + * Close the WebSocket connection with proper cleanup + */ + private closeWebSocket(): void { + if (this.ws) { + try { + logger.debug('Closing WebSocket connection') + + // Remove all event listeners before closing + this.ws.removeAllListeners('open') + this.ws.removeAllListeners('message') + this.ws.removeAllListeners('close') + this.ws.removeAllListeners('error') + this.ws.removeAllListeners('ping') + this.ws.removeAllListeners('pong') + + // Try to close gracefully first + if (this.ws.readyState === WebSocket.OPEN) { + // Set timeout in case close hangs + const closeTimeout = setTimeout(() => { + logger.warn('WebSocket close timed out, forcing termination') + if (this.ws) { + try { + this.ws.terminate() + } catch (e) { + // Ignore errors on terminate after timeout + } + this.ws = undefined + } + }, 1000) + + // Try graceful closure first + this.ws.close(1000, 'Normal Closure') + + // Set up a handler to clear the timeout if close works normally + this.ws.once('close', () => { + clearTimeout(closeTimeout) + }) + } else { + // If not open, just terminate + this.ws.terminate() + } + } catch (error) { + logger.error(`Error closing WebSocket: ${error}`) + } finally { + this.ws = undefined + } + } + } + + /** + * Start the ping interval to keep the connection alive + */ + private startPingInterval(): void { + this.stopPingInterval() + + // Send ping every 30 seconds to keep the connection alive + this.pingInterval = setInterval(() => { + if (this.ws && this.ws.readyState === WebSocket.OPEN) { + logger.debug('Sending ping') + try { + this.ws.ping(crypto.randomBytes(16)) + } catch (error) { + logger.error(`Error sending ping: ${error}`) + } + } else { + // If websocket is no longer open, stop the interval + this.stopPingInterval() + } + }, 30000) + } + + /** + * Stop the ping interval with better error handling + */ + private stopPingInterval(): void { + try { + if (this.pingInterval) { + clearInterval(this.pingInterval) + this.pingInterval = undefined + logger.debug('Ping interval stopped') + } + } catch (error) { + logger.error(`Error stopping ping interval: ${error}`) + this.pingInterval = undefined + } + } + + /** + * Attempt to reconnect to the WebSocket server with better resource management + */ + private async attemptReconnect(): Promise { + if (this.isDisposed) { + logger.debug('LocalProxy is disposed, not attempting reconnect') + return + } + + if (!this.clientToken) { + logger.debug('stop retrying, ws closed manually') + return + } + + if (this.reconnectAttempts >= this.maxReconnectAttempts) { + logger.error('Max reconnect attempts reached') + // Clean up resources when max attempts reached + this.stop() + return + } + + this.reconnectAttempts++ + const delay = this.reconnectInterval * Math.pow(1.5, this.reconnectAttempts - 1) + + logger.debug(`Attempting to reconnect in ${delay}ms (attempt ${this.reconnectAttempts})`) + + // Use a tracked timeout that we can clear if needed + const reconnectTimeoutId = setTimeout(() => { + if (!this.isDisposed) { + void this.connectWebSocket().catch((err) => { + logger.error(`Reconnect failed: ${err}`) + }) + } else { + logger.debug('Reconnect cancelled because LocalProxy was disposed') + } + }, delay) + + // Store the timeout ID so it can be cleared if stop() is called + if (!this.eventHandlers['reconnectTimeouts']) { + this.eventHandlers['reconnectTimeouts'] = [] + } + this.eventHandlers['reconnectTimeouts'].push(reconnectTimeoutId) + } + + /** + * Handle a WebSocket message + * @param data The message data + */ + private handleWebSocketMessage(data: WebSocket.RawData): void { + try { + // Handle binary data + if (Buffer.isBuffer(data)) { + let offset = 0 + + // Process all messages in the buffer + while (offset < data.length) { + // Read the 2-byte length prefix + if (offset + 2 > data.length) { + logger.error('Incomplete message length prefix') + break + } + + const messageLength = data.readUInt16BE(offset) + offset += 2 + + // Check if we have the complete message + if (offset + messageLength > data.length) { + logger.error('Incomplete message data') + break + } + + // Extract the message data + const messageData = data.slice(offset, offset + messageLength) + offset += messageLength + + // Decode and process the message + this.processMessage(messageData) + } + } else { + logger.warn('Received non-buffer WebSocket message') + } + } catch (error) { + logger.error(`Error handling WebSocket message:${error}`) + } + } + + /** + * Process a decoded message + * @param messageData The message data + */ + private processMessage(messageData: Buffer): void { + try { + if (!this.Message) { + logger.error('Protobuf Message type not loaded') + return + } + + // Decode the message + const message = this.Message.decode(messageData) + + // Process based on message type + const typedMessage = message as any + switch (typedMessage.type) { + case MessageType.DATA: + this.handleDataMessage(message) + break + + case MessageType.STREAM_RESET: + this.handleStreamReset(message) + break + + case MessageType.CONNECTION_RESET: + this.handleConnectionReset(message) + break + + case MessageType.SESSION_RESET: + this.handleSessionReset() + break + + case MessageType.SERVICE_IDS: + this.handleServiceIds(message) + break + + default: + logger.debug(`Received message of type ${typedMessage.type}`) + break + } + } catch (error) { + logger.error(`Error processing message:${error}`) + } + } + + /** + * Handle a DATA message + * @param message The message + */ + private handleDataMessage(message: any): void { + const { streamId, connectionId, payload } = message + + // Validate stream ID + if (streamId !== this.currentStreamId) { + logger.warn(`Received data for invalid stream ID: ${streamId}, current: ${this.currentStreamId}`) + return + } + + // Find the connection + const connection = this.tcpConnections.get(connectionId || 1) + if (!connection) { + logger.warn(`Received data for unknown connection ID: ${connectionId}`) + return + } + + logger.debug(`Received data for connection ${connectionId} in stream ${streamId}`) + + // Write data to the TCP socket + if (connection.socket.writable) { + connection.socket.write(Buffer.from(payload)) + } + } + + /** + * Handle a STREAM_RESET message + * @param message The message + */ + private handleStreamReset(message: any): void { + const { streamId } = message + + logger.debug(`Received STREAM_RESET for stream ${streamId}`) + + // Close all connections for this stream + for (const [connectionId, connection] of this.tcpConnections.entries()) { + if (connection.streamId === streamId) { + connection.socket.destroy() + this.tcpConnections.delete(connectionId) + } + } + } + + /** + * Handle a CONNECTION_RESET message + * @param message The message + */ + private handleConnectionReset(message: any): void { + const { streamId, connectionId } = message + + logger.debug(`Received CONNECTION_RESET for connection ${connectionId} in stream ${streamId}`) + + // Close the specific connection + const connection = this.tcpConnections.get(connectionId) + if (connection) { + connection.socket.destroy() + this.tcpConnections.delete(connectionId) + } + } + + /** + * Handle a SESSION_RESET message + */ + private handleSessionReset(): void { + logger.debug('Received SESSION_RESET') + + // Close all connections + for (const connection of this.tcpConnections.values()) { + connection.socket.destroy() + } + this.tcpConnections.clear() + + // Increment stream ID for new connections + this.currentStreamId++ + } + + /** + * Handle a SERVICE_IDS message + * @param message The message + */ + private handleServiceIds(message: any): void { + const { availableServiceIds } = message + + logger.debug(`Received SERVICE_IDS: ${availableServiceIds}`) + + // Validate service IDs + if (Array.isArray(availableServiceIds) && availableServiceIds.length > 0) { + // Use the first service ID + this.serviceId = availableServiceIds[0] + } + } + + /** + * Send a message over the WebSocket + * @param messageType The message type + * @param streamId The stream ID + * @param connectionId The connection ID + * @param payload The payload + */ + private sendMessage(messageType: MessageType, streamId: number, connectionId: number, payload?: Buffer): void { + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { + logger.warn('WebSocket not connected, cannot send message') + return + } + + if (!this.Message) { + logger.error('Protobuf Message type not loaded') + return + } + + try { + // Create the message + const message = { + type: messageType, + streamId, + connectionId, + serviceId: this.serviceId, + } + + // Add payload if provided + const typedMessage: any = message + if (payload) { + typedMessage.payload = payload + } + + // Verify and encode the message + const err = this.Message.verify(message) + if (err) { + throw new Error(`Invalid message: ${err}`) + } + + const encodedMessage = this.Message.encode(this.Message.create(message)).finish() + + // Create the frame with 2-byte length prefix + const frameLength = encodedMessage.length + const frame = Buffer.alloc(2 + frameLength) + + // Write the length prefix + frame.writeUInt16BE(frameLength, 0) + + // Copy the encoded message + Buffer.from(encodedMessage).copy(frame, 2) + + // Send the frame + if (this.ws?.readyState === WebSocket.OPEN) { + this.ws.send(frame) + } else { + logger.warn('WebSocket connection lost before sending message') + } + } catch (error) { + logger.error(`Error sending message: ${error}`) + } + } + + /** + * Send a STREAM_START message + * @param streamId The stream ID + * @param connectionId The connection ID + */ + private sendStreamStart(streamId: number, connectionId: number): void { + logger.debug(`Sending STREAM_START for stream ${streamId}, connection ${connectionId}`) + this.sendMessage(MessageType.STREAM_START, streamId, connectionId) + } + + /** + * Send a CONNECTION_START message + * @param streamId The stream ID + * @param connectionId The connection ID + */ + private sendConnectionStart(streamId: number, connectionId: number): void { + logger.debug(`Sending CONNECTION_START for stream ${streamId}, connection ${connectionId}`) + this.sendMessage(MessageType.CONNECTION_START, streamId, connectionId) + } + + /** + * Send a CONNECTION_RESET message + * @param streamId The stream ID + * @param connectionId The connection ID + */ + private sendConnectionReset(streamId: number, connectionId: number): void { + logger.debug(`Sending CONNECTION_RESET for stream ${streamId}, connection ${connectionId}`) + this.sendMessage(MessageType.CONNECTION_RESET, streamId, connectionId) + } + + /** + * Send data over the WebSocket + * @param streamId The stream ID + * @param connectionId The connection ID + * @param data The data to send + */ + private sendData(streamId: number, connectionId: number, data: Buffer): void { + // Split data into chunks if it exceeds the maximum payload size (63kb) + const maxChunkSize = 63 * 1024 // 63kb + + for (let offset = 0; offset < data.length; offset += maxChunkSize) { + const chunk = data.slice(offset, offset + maxChunkSize) + this.sendMessage(MessageType.DATA, streamId, connectionId, chunk) + } + } +} diff --git a/packages/core/src/lambda/remoteDebugging/utils.ts b/packages/core/src/lambda/remoteDebugging/utils.ts new file mode 100644 index 00000000000..6f7256f9f61 --- /dev/null +++ b/packages/core/src/lambda/remoteDebugging/utils.ts @@ -0,0 +1,27 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import IoTSecureTunneling from 'aws-sdk/clients/iotsecuretunneling' +import { DefaultLambdaClient } from '../../shared/clients/lambdaClient' +import { getUserAgent } from '../../shared/telemetry/util' +import globals from '../../shared/extensionGlobals' + +const customUserAgentBase = 'LAMBDA-DEBUG/1.0.0' + +export function getLambdaClientWithAgent(region: string): DefaultLambdaClient { + const customUserAgent = `${customUserAgentBase} ${getUserAgent({ includePlatform: true, includeClientId: true })}` + return new DefaultLambdaClient(region, customUserAgent) +} + +export function getIoTSTClientWithAgent(region: string): Promise { + const customUserAgent = `${customUserAgentBase} ${getUserAgent({ includePlatform: true, includeClientId: true })}` + return globals.sdkClientBuilder.createAwsService( + IoTSecureTunneling, + { + customUserAgent, + }, + region + ) +} diff --git a/packages/core/src/lambda/vue/remoteInvoke/invokeLambda.ts b/packages/core/src/lambda/vue/remoteInvoke/invokeLambda.ts index aafde327d7f..9e027bde2bc 100644 --- a/packages/core/src/lambda/vue/remoteInvoke/invokeLambda.ts +++ b/packages/core/src/lambda/vue/remoteInvoke/invokeLambda.ts @@ -7,7 +7,7 @@ import { _Blob } from 'aws-sdk/clients/lambda' import { readFileSync } from 'fs' // eslint-disable-line no-restricted-imports import * as _ from 'lodash' import * as vscode from 'vscode' -import { DefaultLambdaClient, LambdaClient } from '../../../shared/clients/lambdaClient' +import { LambdaClient } from '../../../shared/clients/lambdaClient' import * as picker from '../../../shared/ui/picker' import { ExtContext } from '../../../shared/extensions' @@ -19,7 +19,7 @@ import { getSampleLambdaPayloads, SampleRequest } from '../../utils' import * as nls from 'vscode-nls' import { VueWebview } from '../../../webviews/main' -import { telemetry, Result } from '../../../shared/telemetry/telemetry' +import { telemetry, Result, Runtime } from '../../../shared/telemetry/telemetry' import { runSamCliRemoteTestEvents, SamCliRemoteTestEventsParameters, @@ -29,6 +29,13 @@ import { getSamCliContext } from '../../../shared/sam/cli/samCliContext' import { ToolkitError } from '../../../shared/errors' import { basename } from 'path' import { decodeBase64 } from '../../../shared/utilities/textUtilities' +import { DebugConfig, RemoteDebugController, revertExistingConfig } from '../../remoteDebugging/ldkController' +import { getCachedLocalPath, openLambdaFile, runDownloadLambda } from '../../commands/downloadLambda' +import { getLambdaHandlerFile } from '../../../awsService/appBuilder/utils' +import { runUploadDirectory } from '../../commands/uploadLambda' +import fs from '../../../shared/fs/fs' +import { showConfirmationMessage, showMessage } from '../../../shared/utilities/messages' +import { getLambdaClientWithAgent } from '../../remoteDebugging/utils' const localize = nls.loadMessageBundle() @@ -48,19 +55,75 @@ export interface InitialData { Source?: string StackName?: string LogicalId?: string + Runtime?: string + LocalRootPath?: string + LambdaFunctionNode?: LambdaFunctionNode + supportCodeDownload?: boolean + runtimeSupportsRemoteDebug?: boolean + remoteDebugLayer?: string | undefined } -export interface RemoteInvokeData { - initialData: InitialData +// Debug configuration sub-interface +export interface DebugConfiguration { + debugPort: number + localRootPath: string + remoteRootPath: string + shouldPublishVersion: boolean + lambdaTimeout: number + otherDebugParams: string +} + +// Debug state sub-interface +export interface DebugState { + isDebugging: boolean + debugTimer: number | undefined + debugTimeRemaining: number + showDebugTimer: boolean + handlerFileAvailable: boolean + remoteDebuggingEnabled: boolean +} + +// Runtime-specific debug settings sub-interface +export interface RuntimeDebugSettings { + // Node.js specific + sourceMapEnabled: boolean + skipFiles: string + outFiles: string | undefined + // Python specific + justMyCode: boolean + // Java specific + projectName: string +} + +// UI state sub-interface +export interface UIState { + isCollapsed: boolean + showNameInput: boolean + payload: string +} + +// Payload/Event handling sub-interface +export interface PayloadData { selectedSampleRequest: string sampleText: string selectedFile: string selectedFilePath: string selectedTestEvent: string - payload: string - showNameInput: boolean newTestEventName: string - selectedFunction: string +} + +export interface RemoteInvokeData { + initialData: InitialData + debugConfig: DebugConfiguration + debugState: DebugState + runtimeSettings: RuntimeDebugSettings + uiState: UIState + payloadData: PayloadData +} + +// Event types for communicating state between backend and frontend +export type StateChangeEvent = { + isDebugging?: boolean } interface SampleQuickPickItem extends vscode.QuickPickItem { filename: string @@ -70,6 +133,19 @@ export class RemoteInvokeWebview extends VueWebview { public static readonly sourcePath: string = 'src/lambda/vue/remoteInvoke/index.js' public readonly id = 'remoteInvoke' + // Event emitter for state changes that need to be synchronized with the frontend + public readonly onStateChange = new vscode.EventEmitter() + + // Backend timer that will continue running even when the webview loses focus + private debugTimerHandle: NodeJS.Timeout | undefined + private debugTimeRemaining: number = 0 + private isInvoking: boolean = false + private debugging: boolean = false + private watcherDisposable: vscode.Disposable | undefined + private fileWatcherDisposable: vscode.Disposable | undefined + private handlerFileAvailable: boolean = false + private isStartingDebug: boolean = false + private handlerFile: string | undefined public constructor( private readonly channel: vscode.OutputChannel, private readonly client: LambdaClient, @@ -79,17 +155,137 @@ export class RemoteInvokeWebview extends VueWebview { } public init() { + this.watcherDisposable = vscode.debug.onDidTerminateDebugSession(async (session: vscode.DebugSession) => { + this.resetServerState() + }) return this.data } - public async invokeLambda(input: string, source?: string): Promise { + public resetServerState() { + this.stopDebugTimer() + this.debugging = false + this.isInvoking = false + this.isStartingDebug = false + this.onStateChange.fire({ + isDebugging: false, + }) + } + + public async disposeServer() { + this.watcherDisposable?.dispose() + this.fileWatcherDisposable?.dispose() + if (this.debugging && RemoteDebugController.instance.isDebugging) { + await this.stopDebugging() + } + this.dispose() + } + + private setupFileWatcher() { + // Dispose existing watcher if any + this.fileWatcherDisposable?.dispose() + + if (!this.data.LocalRootPath) { + return + } + + // Create a file system watcher for the local root path + const pattern = new vscode.RelativePattern(this.data.LocalRootPath, '**/*') + const watcher = vscode.workspace.createFileSystemWatcher(pattern) + + // Set up event handlers for file changes + const handleFileChange = async () => { + const result = await showMessage( + 'info', + localize( + 'AWS.lambda.remoteInvoke.codeChangesDetected', + 'Code changes detected in the local directory. Would you like to update the Lambda function {0}@{1}?', + this.data.FunctionName, + this.data.FunctionRegion + ), + ['Yes', 'No'] + ) + + if (result === 'Yes') { + try { + if (this.data.LambdaFunctionNode && this.data.LocalRootPath) { + const lambdaFunction = { + name: this.data.FunctionName, + region: this.data.FunctionRegion, + configuration: this.data.LambdaFunctionNode.configuration, + } + await runUploadDirectory(lambdaFunction, 'zip', vscode.Uri.file(this.data.LocalRootPath)) + } + } catch (error) { + throw ToolkitError.chain( + error, + localize('AWS.lambda.remoteInvoke.updateFailed', 'Failed to update Lambda function') + ) + } + } + } + + // Listen for file changes, creations, and deletions + watcher.onDidChange(handleFileChange) + watcher.onDidCreate(handleFileChange) + watcher.onDidDelete(handleFileChange) + + // Store the disposable so we can clean it up later + this.fileWatcherDisposable = watcher + } + + // Method to start the backend timer + public startDebugTimer() { + // Clear any existing timer + this.stopDebugTimer() + + this.debugTimeRemaining = 60 + + // Create a new timer that ticks every second + this.debugTimerHandle = setInterval(async () => { + this.debugTimeRemaining-- + + // When timer reaches zero, stop debugging + if (this.debugTimeRemaining <= 0) { + await this.handleTimerExpired() + } + }, 1000) + } + + // Method to stop the timer + public stopDebugTimer() { + if (this.debugTimerHandle) { + clearInterval(this.debugTimerHandle) + this.debugTimerHandle = undefined + this.debugTimeRemaining = 0 + } + } + + // Handler for timer expiration + private async handleTimerExpired() { + await this.stopDebugging() + } + + public async invokeLambda(input: string, source?: string, remoteDebugEnabled: boolean = false): Promise { let result: Result = 'Succeeded' + let qualifier: string | undefined = undefined + // if debugging, focus on the first editor + if (remoteDebugEnabled && RemoteDebugController.instance.isDebugging) { + await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup') + qualifier = RemoteDebugController.instance.qualifier + } + + this.isInvoking = true + + // If debugging is active, reset the timer during invoke + if (RemoteDebugController.instance.isDebugging) { + this.stopDebugTimer() + } this.channel.show() this.channel.appendLine('Loading response...') try { - const funcResponse = await this.client.invoke(this.data.FunctionArn, input) + const funcResponse = await this.client.invoke(this.data.FunctionArn, input, qualifier) const logs = funcResponse.LogResult ? decodeBase64(funcResponse.LogResult) : '' const payload = funcResponse.Payload ? funcResponse.Payload : JSON.stringify({}) @@ -107,13 +303,28 @@ export class RemoteInvokeWebview extends VueWebview { this.channel.appendLine('') result = 'Failed' } finally { - telemetry.lambda_invokeRemote.emit({ result, passive: false, source: source }) + telemetry.lambda_invokeRemote.emit({ + result, + passive: false, + source: source, + runtimeString: this.data.Runtime, + action: remoteDebugEnabled ? 'debug' : 'invoke', + }) + + // Update the session state to indicate we've finished invoking + this.isInvoking = false + + // If debugging is active, restart the timer + if (RemoteDebugController.instance.isDebugging) { + this.startDebugTimer() + } + this.channel.show() } } public async promptFile() { const fileLocations = await vscode.window.showOpenDialog({ - openLabel: 'Open', + openLabel: localize('AWS.lambda.remoteInvoke.open', 'Open'), }) if (!fileLocations || fileLocations.length === 0) { @@ -129,8 +340,66 @@ export class RemoteInvokeWebview extends VueWebview { } } catch (e) { getLogger().error('readFileSync: Failed to read file at path %s %O', fileLocations[0].fsPath, e) - throw ToolkitError.chain(e, 'Failed to read selected file') + throw ToolkitError.chain( + e, + localize('AWS.lambda.remoteInvoke.failedToReadFile', 'Failed to read selected file') + ) + } + } + + public async promptFolder(): Promise { + const fileLocations = await vscode.window.showOpenDialog({ + openLabel: localize('AWS.lambda.remoteInvoke.open', 'Open'), + canSelectFolders: true, + canSelectFiles: false, + canSelectMany: false, + }) + + if (!fileLocations || fileLocations.length === 0) { + return undefined + } + this.data.LocalRootPath = fileLocations[0].fsPath + // try to find the handler file in this folder, open it if not opened already + if (!(await this.tryOpenHandlerFile())) { + const warning = localize( + 'AWS.lambda.remoteInvoke.handlerFileNotFound', + 'Handler {0} not found in selected location. Please select the folder that contains the copy of your handler file', + this.data.LambdaFunctionNode?.configuration.Handler + ) + getLogger().warn(warning) + void vscode.window.showWarningMessage(warning) + } + return fileLocations[0].fsPath + } + + public async tryOpenHandlerFile(path?: string, watchForUpdates: boolean = true): Promise { + this.handlerFile = undefined + if (path) { + // path is provided, override init path + this.data.LocalRootPath = path } + // init path or node not available + if (!this.data.LocalRootPath || !this.data.LambdaFunctionNode) { + return false + } + + const handlerFile = await getLambdaHandlerFile( + vscode.Uri.file(this.data.LocalRootPath), + '', + this.data.LambdaFunctionNode?.configuration.Handler ?? '', + this.data.Runtime ?? 'unknown' + ) + if (!handlerFile || !(await fs.exists(handlerFile))) { + this.handlerFileAvailable = false + return false + } + this.handlerFileAvailable = true + if (watchForUpdates) { + this.setupFileWatcher() + } + await openLambdaFile(handlerFile.fsPath) + this.handlerFile = handlerFile.fsPath + return true } public async loadFile(fileLocations: string) { @@ -152,7 +421,10 @@ export class RemoteInvokeWebview extends VueWebview { } } catch (e) { getLogger().error('readFileSync: Failed to read file at path %s %O', fileLocation.fsPath, e) - throw ToolkitError.chain(e, 'Failed to read selected file') + throw ToolkitError.chain( + e, + localize('AWS.lambda.remoteInvoke.failedToReadFile', 'Failed to read selected file') + ) } } @@ -225,12 +497,234 @@ export class RemoteInvokeWebview extends VueWebview { return sample } catch (err) { getLogger().error('Error getting manifest data..: %O', err as Error) - throw ToolkitError.chain(err, 'getting manifest data') + throw ToolkitError.chain( + err, + localize('AWS.lambda.remoteInvoke.gettingManifestData', 'getting manifest data') + ) } } -} -const Panel = VueWebview.compilePanel(RemoteInvokeWebview) + // Download lambda code and update the local root path + public async downloadRemoteCode(): Promise { + return await telemetry.lambda_import.run(async (span) => { + span.record({ runtime: this.data.Runtime as Runtime | undefined, source: 'RemoteDebug' }) + try { + if (this.data.LambdaFunctionNode) { + const output = await runDownloadLambda(this.data.LambdaFunctionNode, true) + if (output instanceof vscode.Uri) { + this.data.LocalRootPath = output.fsPath + this.handlerFileAvailable = true + this.setupFileWatcher() + + return output.fsPath + } + } else { + getLogger().error( + localize( + 'AWS.lambda.remoteInvoke.lambdaFunctionNodeUndefined', + 'LambdaFunctionNode is undefined' + ) + ) + } + return undefined + } catch (error) { + throw ToolkitError.chain( + error, + localize('AWS.lambda.remoteInvoke.failedToDownloadCode', 'Failed to download remote code') + ) + } + }) + } + + // this serves as a lock for invoke + public checkReadyToInvoke(): boolean { + if (this.isInvoking) { + void vscode.window.showWarningMessage( + localize( + 'AWS.lambda.remoteInvoke.invokeInProgress', + 'A remote invoke is already in progress, please wait for previous invoke, or remove debug setup' + ) + ) + return false + } + if (this.isStartingDebug) { + void vscode.window.showWarningMessage( + localize( + 'AWS.lambda.remoteInvoke.debugSetupInProgress', + 'A debugger setup is already in progress, please wait for previous setup to complete, or remove debug setup' + ) + ) + } + return true + } + + // this check is run when user click remote invoke with remote debugging checked + public async checkReadyToDebug(config: DebugConfig): Promise { + if (!this.data.LambdaFunctionNode) { + return false + } + + if (!this.handlerFileAvailable) { + const result = await showConfirmationMessage({ + prompt: localize( + 'AWS.lambda.remoteInvoke.handlerFileNotLocated', + 'The handler file cannot be located in the specified Local Root Path. As a result, remote debugging will not pause at breakpoints.' + ), + confirm: 'Continue Anyway', + cancel: 'Cancel', + type: 'warning', + }) + if (!result) { + return false + } + } + // check if snapstart is on and we are publishing a version + if ( + config.shouldPublishVersion && + this.data.LambdaFunctionNode.configuration.SnapStart?.ApplyOn === 'PublishedVersions' + ) { + const result = await showConfirmationMessage({ + prompt: localize( + 'AWS.lambda.remoteInvoke.snapstartWarning', + "This function has Snapstart enabled. If you use Remote Debug with the 'publish version' setting, you'll experience notable delays. For faster debugging, consider disabling the 'publish version' option." + ), + confirm: 'Continue Anyway', + cancel: 'Cancel', + type: 'warning', + }) + if (!result) { + // didn't confirm + getLogger().warn( + localize('AWS.lambda.remoteInvoke.userCanceledSnapstart', 'User canceled Snapstart confirm') + ) + return false + } + } + + // ready to debug + return true + } + + public async startDebugging(config: DebugConfig): Promise { + if (!this.data.LambdaFunctionNode) { + return false + } + if (!(await this.checkReadyToDebug(config))) { + return false + } + this.isStartingDebug = true + try { + await RemoteDebugController.instance.startDebugging(this.data.FunctionArn, this.data.Runtime ?? 'unknown', { + ...config, + handlerFile: this.handlerFile, + }) + } catch (e) { + throw ToolkitError.chain( + e, + localize('AWS.lambda.remoteInvoke.failedToStartDebugging', 'Failed to start debugging') + ) + } finally { + this.isStartingDebug = false + } + + this.startDebugTimer() + this.debugging = this.isLDKDebugging() + return this.debugging + } + + public async stopDebugging(): Promise { + if (this.isLDKDebugging()) { + this.resetServerState() + await RemoteDebugController.instance.stopDebugging() + } + this.debugging = this.isLDKDebugging() + return this.debugging + } + + public isLDKDebugging(): boolean { + return RemoteDebugController.instance.isDebugging + } + + public isWebViewDebugging(): boolean { + return this.debugging + } + + public getIsInvoking(): boolean { + return this.isInvoking + } + + public getDebugTimeRemaining(): number { + return this.debugTimeRemaining + } + + public getLocalPath(): string { + return this.data.LocalRootPath ?? '' + } + + public getHandlerAvailable(): boolean { + return this.handlerFileAvailable + } + + // prestatus check run at checkbox click + public async debugPreCheck(): Promise { + return await telemetry.lambda_remoteDebugPrecheck.run(async (span) => { + span.record({ runtimeString: this.data.Runtime, source: 'webview' }) + if (!this.debugging && RemoteDebugController.instance.isDebugging) { + // another debug session in progress + const result = await showConfirmationMessage({ + prompt: localize( + 'AWS.lambda.remoteInvoke.debugSessionActive', + 'A remote debug session is already active. Stop that for this new session?' + ), + confirm: 'Stop Previous Session', + cancel: 'Cancel', + type: 'warning', + }) + + if (result) { + // Stop the previous session + if (await this.stopDebugging()) { + getLogger().error( + localize( + 'AWS.lambda.remoteInvoke.failedToStopPreviousSession', + 'Failed to stop previous debug session.' + ) + ) + return false + } + } else { + // user canceled, Do nothing + return false + } + } + + const result = await RemoteDebugController.instance.installDebugExtension(this.data.Runtime) + if (!result && result === false) { + // install failed + return false + } + + await revertExistingConfig() + + // Check if the function ARN is in the cache and try to open handler file + const cachedPath = getCachedLocalPath(this.data.FunctionArn) + // only check cache if not comming from appbuilder + if (cachedPath && !this.data.LambdaFunctionNode?.localDir) { + getLogger().debug( + `lambda: found cached local path for function ARN: ${this.data.FunctionArn} -> ${cachedPath}` + ) + await this.tryOpenHandlerFile(cachedPath, true) + } + + // this is comming from appbuilder + if (this.data.LambdaFunctionNode?.localDir) { + await this.tryOpenHandlerFile(undefined, false) + } + + return true + }) + } +} export async function invokeRemoteLambda( context: ExtContext, @@ -247,9 +741,22 @@ export async function invokeRemoteLambda( } ) { const inputs = await getSampleLambdaPayloads() - const resource: any = params.functionNode + const resource: LambdaFunctionNode = params.functionNode const source: string = params.source || 'AwsExplorerRemoteInvoke' - const client = new DefaultLambdaClient(resource.regionCode) + const client = getLambdaClientWithAgent(resource.regionCode) + + const Panel = VueWebview.compilePanel(RemoteInvokeWebview) + + // Initialize support and debugging capabilities + const runtime = resource.configuration.Runtime ?? 'unknown' + const region = resource.regionCode + const supportCodeDownload = RemoteDebugController.instance.supportCodeDownload(runtime) + const runtimeSupportsRemoteDebug = RemoteDebugController.instance.supportRuntimeRemoteDebug(runtime) + const remoteDebugLayer = RemoteDebugController.instance.getRemoteDebugLayer( + region, + resource.configuration.Architectures + ) + const wv = new Panel(context.extensionContext, context.outputChannel, client, { FunctionName: resource.configuration.FunctionName ?? '', FunctionArn: resource.configuration.FunctionArn ?? '', @@ -257,9 +764,22 @@ export async function invokeRemoteLambda( InputSamples: inputs, TestEvents: [], Source: source, + Runtime: runtime, + LocalRootPath: params.functionNode.localDir, + LambdaFunctionNode: params.functionNode, + supportCodeDownload: supportCodeDownload, + runtimeSupportsRemoteDebug: runtimeSupportsRemoteDebug, + remoteDebugLayer: remoteDebugLayer, }) + // focus on first group so wv will show up in the side + await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup') - await wv.show({ + const activePanel = await wv.show({ title: localize('AWS.invokeLambda.title', 'Invoke Lambda {0}', resource.configuration.FunctionName), + viewColumn: vscode.ViewColumn.Beside, + }) + + activePanel.onDidDispose(async () => { + await wv.server.disposeServer() }) } diff --git a/packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.css b/packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.css index 99f124e6b0c..bb7d5054bf2 100644 --- a/packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.css +++ b/packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.css @@ -1,6 +1,8 @@ .Icontainer { margin-inline: auto; - margin-top: 5rem; + margin-top: 2rem; + width: 100%; + min-width: 650px; } h1 { @@ -8,17 +10,35 @@ h1 { margin-bottom: 20px; } +/* Remove fixed width for divs to allow responsive behavior */ div { - width: 521px; + width: 100%; } .form-row { display: grid; grid-template-columns: 150px 1fr; margin-bottom: 10px; + align-items: center; +} + +.form-row-no-align { + display: grid; + grid-template-columns: 150px 1fr; + margin-bottom: 10px; +} + +.form-double-row { + display: grid; + grid-template-rows: 20px 1fr; + margin-inline: 0px; + padding: 0px 0px; + align-items: center; } + .form-row-select { - width: 387px; + width: 100%; + max-width: 387px; height: 28px; border: 1px; border-radius: 5px; @@ -29,16 +49,18 @@ div { .dynamic-span { white-space: nowrap; text-overflow: initial; - overflow: scroll; - width: 381px; - height: 28px; + overflow: auto; + width: 100%; + max-width: 381px; + height: auto; font-weight: 500; font-size: 13px; line-height: 15.51px; } .form-row-event-select { - width: 244px; + width: 100%; + max-width: 244px; height: 28px; margin-bottom: 15px; margin-left: 8px; @@ -52,6 +74,23 @@ div { } label { + font-weight: 500; + font-size: 14px; + margin-right: 10px; +} + +info { + color: var(--vscode-descriptionForeground); + font-weight: 500; + font-size: 13px; + margin-right: 10px; + text-wrap-mode: nowrap; +} + +info-wrap { + color: var(--vscode-descriptionForeground); + font-weight: 500; + font-size: 13px; margin-right: 10px; } @@ -65,6 +104,9 @@ textarea { color: var(--vscode-settings-textInputForeground); background: var(--vscode-settings-textInputBackground); border: 1px solid var(--vscode-settings-textInputBorder); + width: 100%; + box-sizing: border-box; + resize: none; } .payload-options-button { @@ -98,6 +140,17 @@ textarea { cursor: pointer; } +.button-theme-inline { + color: var(--vscode-button-secondaryForeground); + background: var(--vscode-button-secondaryBackground); + border: 1px solid var(--vscode-button-border); + padding: 4px 6px; +} +.button-theme-inline:hover:not(:disabled) { + background: var(--vscode-button-secondaryHoverBackground); + cursor: pointer; +} + .payload-options-buttons { display: flex; align-items: center; @@ -130,3 +183,85 @@ textarea { align-items: center; margin-bottom: 0.5rem; } + +.debug-timer { + padding: 5px 10px; + background-color: var(--vscode-editorWidget-background); + border-radius: 4px; + font-weight: 500; +} + +.collapsible-section { + margin: 15px 0; + border: 1px solid var(--vscode-widget-border); + border-radius: 4px; +} + +.collapsible-header { + padding: 8px 12px; + background-color: var(--vscode-sideBarSectionHeader-background); + cursor: pointer; + font-weight: 500; + max-width: 96%; +} + +.collapsible-content { + padding: 10px; + border-top: 1px solid var(--vscode-widget-border); + max-width: 96%; +} + +/* Ensure buttons in the same line are properly spaced */ +.button-container { + display: flex; + gap: 5px; +} + +/* For buttons that should be disabled */ +button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* Validation error styles */ +.input-error { + border: 1px solid var(--vscode-inputValidation-errorBorder) !important; + background-color: var(--vscode-inputValidation-errorBackground) !important; +} + +.error-message { + color: var(--vscode-inputValidation-errorForeground); + font-size: 12px; + margin-top: 4px; + font-weight: 400; + line-height: 1.2; +} + +/* Enhanced styling for remote debug checkbox to make it more obvious in dark mode */ +.remote-debug-checkbox { + width: 18px !important; + height: 18px !important; + accent-color: var(--vscode-checkbox-foreground); + border: 2px solid var(--vscode-checkbox-border) !important; + border-radius: 3px !important; + background-color: var(--vscode-checkbox-background) !important; + border-color: var(--vscode-checkbox-selectBorder) !important; + cursor: pointer; +} + +.remote-debug-checkbox:checked { + background-color: var(--vscode-checkbox-selectBackground) !important; + border-color: var(--vscode-checkbox-selectBorder) !important; +} + +.remote-debug-checkbox:disabled { + opacity: 0.6; + cursor: not-allowed; + border-color: var(--vscode-checkbox-border); + background-color: var(--vscode-input-background); +} + +.remote-debug-checkbox:focus { + outline: 2px solid var(--vscode-focusBorder); + outline-offset: 2px; +} diff --git a/packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.vue b/packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.vue index 8309fce6990..1743fd4ef00 100644 --- a/packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.vue +++ b/packages/core/src/lambda/vue/remoteInvoke/remoteInvoke.vue @@ -6,124 +6,402 @@ diff --git a/packages/core/src/lambda/vue/remoteInvoke/remoteInvokeFrontend.ts b/packages/core/src/lambda/vue/remoteInvoke/remoteInvokeFrontend.ts index 301b47603e9..b2253a46fd2 100644 --- a/packages/core/src/lambda/vue/remoteInvoke/remoteInvokeFrontend.ts +++ b/packages/core/src/lambda/vue/remoteInvoke/remoteInvokeFrontend.ts @@ -21,54 +21,202 @@ const defaultInitialData = { TestEvents: [], FunctionStack: '', Source: '', + LambdaFunctionNode: undefined, + supportCodeDownload: true, + runtimeSupportsRemoteDebug: true, + remoteDebugLayer: '', } export default defineComponent({ - async created() { - this.initialData = (await client.init()) ?? this.initialData - if (this.initialData.FunctionArn && this.initialData.FunctionRegion) { - this.initialData.TestEvents = await client.listRemoteTestEvents( - this.initialData.FunctionArn, - this.initialData.FunctionRegion - ) - } - }, - data(): RemoteInvokeData { return { initialData: { ...defaultInitialData }, - selectedSampleRequest: '', - sampleText: '{}', - selectedFile: '', - selectedFilePath: '', - payload: 'sampleEvents', - selectedTestEvent: '', - showNameInput: false, - newTestEventName: '', - selectedFunction: 'selectedFunction', + debugConfig: { + debugPort: 9229, + localRootPath: '', + remoteRootPath: '/var/task', + shouldPublishVersion: true, + lambdaTimeout: 900, + otherDebugParams: '', + }, + debugState: { + isDebugging: false, + debugTimer: undefined, + debugTimeRemaining: 60, + showDebugTimer: false, + handlerFileAvailable: false, + remoteDebuggingEnabled: false, + }, + runtimeSettings: { + sourceMapEnabled: true, + skipFiles: '/var/runtime/node_modules/**/*.js,/**', + justMyCode: true, + projectName: '', + outFiles: undefined, + }, + uiState: { + isCollapsed: true, + showNameInput: false, + payload: 'sampleEvents', + }, + payloadData: { + selectedSampleRequest: '', + sampleText: '{}', + selectedFile: '', + selectedFilePath: '', + selectedTestEvent: '', + newTestEventName: '', + }, } }, + + async created() { + // Initialize data from backend + this.initialData = (await client.init()) ?? this.initialData + this.debugConfig.localRootPath = this.initialData.LocalRootPath ?? '' + + // Register for state change events from the backend + void client.onStateChange(async () => { + await this.syncStateFromWorkspace() + }) + + // Check for existing session state and load it + await this.syncStateFromWorkspace() + }, + + computed: { + // Auto-adjust textarea rows based on content + textareaRows(): number { + if (!this.payloadData.sampleText) { + return 5 // Default minimum rows + } + + // Count line breaks to determine basic row count + const lineCount = this.payloadData.sampleText.split('\n').length + let additionalLine = 0 + for (const line of this.payloadData.sampleText.split('\n')) { + if (line.length > 60) { + additionalLine += Math.floor(line.length / 60) + } + } + + // Use the larger of line count or estimated lines, with min 5 and max 20 + const calculatedRows = lineCount + additionalLine + return Math.max(5, Math.min(50, calculatedRows)) + }, + + // Validation computed properties + debugPortError(): string { + if (this.debugConfig.debugPort !== null && this.debugConfig.debugPort !== undefined) { + const port = Number(this.debugConfig.debugPort) + if (isNaN(port) || port < 1 || port > 65535) { + return 'Debug port must be between 1 and 65535' + } + } + return '' + }, + + otherDebugParamsError(): string { + if (this.debugConfig.otherDebugParams && this.debugConfig.otherDebugParams.trim() !== '') { + try { + JSON.parse(this.debugConfig.otherDebugParams) + } catch (error) { + return 'Other Debug Params must be a valid JSON object' + } + } + return '' + }, + + lambdaTimeoutError(): string { + if (this.debugConfig.lambdaTimeout !== undefined) { + const timeout = Number(this.debugConfig.lambdaTimeout) + if (isNaN(timeout) || timeout < 1 || timeout > 900) { + return 'Timeout override must be between 1 and 900 seconds' + } + } + return '' + }, + + // user can override the default provided layer and bring their own layer + // this is useful to support function with code signing config + lambdaLayerError(): string { + if (this.initialData.remoteDebugLayer && this.initialData.remoteDebugLayer.trim() !== '') { + const layerArn = this.initialData.remoteDebugLayer.trim() + + // Validate Lambda layer ARN format + // Expected format: arn:aws:lambda:region:account-id:layer:layer-name:version + const layerArnRegex = /^arn:aws:lambda:[a-z0-9-]+:\d{12}:layer:[a-zA-Z0-9-_]+:\d+$/ + + if (!layerArnRegex.test(layerArn)) { + return 'Layer ARN must be in the format: arn:aws:lambda:::layer::' + } + + // Extract region from ARN to validate it matches the function region + const arnParts = layerArn.split(':') + if (arnParts.length >= 4) { + const layerRegion = arnParts[3] + if (this.initialData.FunctionRegion && layerRegion !== this.initialData.FunctionRegion) { + return `Layer region (${layerRegion}) must match function region (${this.initialData.FunctionRegion})` + } + } + } + return '' + }, + }, + methods: { + // Runtime detection computed properties based on the runtime string + hasRuntimePrefix(prefix: string): boolean { + const runtime = this.initialData.Runtime || '' + return runtime.startsWith(prefix) + }, + // Sync state from workspace storage + async syncStateFromWorkspace() { + try { + // Update debugging state + this.debugState.isDebugging = await client.isWebViewDebugging() + this.debugConfig.localRootPath = await client.getLocalPath() + this.debugState.handlerFileAvailable = await client.getHandlerAvailable() + // Get current session state + + if (this.debugState.isDebugging) { + // Update invoke button state based on session + const isInvoking = await client.getIsInvoking() + + // If debugging is active and we're not showing the timer, + // calculate and show remaining time + this.clearDebugTimer() + if (this.debugState.isDebugging && !isInvoking) { + await this.startDebugTimer() + } + } else { + this.clearDebugTimer() + // no debug session + } + } catch (error) { + console.error('Failed to sync state from workspace:', error) + } + }, async newSelection() { const eventData = { - name: this.selectedTestEvent, + name: this.payloadData.selectedTestEvent, region: this.initialData.FunctionRegion, arn: this.initialData.FunctionArn, } const resp = await client.getRemoteTestEvents(eventData) - this.sampleText = JSON.stringify(JSON.parse(resp), undefined, 4) + this.payloadData.sampleText = JSON.stringify(JSON.parse(resp), undefined, 4) }, async saveEvent() { const eventData = { - name: this.newTestEventName, - event: this.sampleText, + name: this.payloadData.newTestEventName, + event: this.payloadData.sampleText, region: this.initialData.FunctionRegion, arn: this.initialData.FunctionArn, } await client.createRemoteTestEvents(eventData) - this.showNameInput = false - this.newTestEventName = '' - this.selectedTestEvent = eventData.name + this.uiState.showNameInput = false + this.payloadData.newTestEventName = '' + this.payloadData.selectedTestEvent = eventData.name this.initialData.TestEvents = await client.listRemoteTestEvents( this.initialData.FunctionArn, this.initialData.FunctionRegion @@ -77,46 +225,179 @@ export default defineComponent({ async promptForFileLocation() { const resp = await client.promptFile() if (resp) { - this.selectedFile = resp.selectedFile - this.selectedFilePath = resp.selectedFilePath + this.payloadData.selectedFile = resp.selectedFile + this.payloadData.selectedFilePath = resp.selectedFilePath + } + }, + async promptForFolderLocation() { + const resp = await client.promptFolder() + if (resp) { + this.debugConfig.localRootPath = resp + this.debugState.handlerFileAvailable = await client.getHandlerAvailable() } }, + onFileChange(event: Event) { const input = event.target as HTMLInputElement if (input.files && input.files.length > 0) { const file = input.files[0] - this.selectedFile = file.name + this.payloadData.selectedFile = file.name // Use Blob.text() to read the file as text file.text() .then((text) => { - this.sampleText = text + this.payloadData.sampleText = text }) .catch((error) => { console.error('Error reading file:', error) }) } }, + async debugPreCheck() { + if (!this.debugState.remoteDebuggingEnabled) { + // don't check if unchecking + this.debugState.remoteDebuggingEnabled = false + if (this.debugState.isDebugging) { + await client.stopDebugging() + } + } else { + // check when user is checking box + this.debugState.remoteDebuggingEnabled = await client.debugPreCheck() + this.debugConfig.localRootPath = await client.getLocalPath() + this.debugState.handlerFileAvailable = await client.getHandlerAvailable() + } + }, showNameField() { if (this.initialData.FunctionRegion || this.initialData.FunctionRegion) { - this.showNameInput = true + this.uiState.showNameInput = true } }, async sendInput() { + // Tell the backend to set the button state. This state is maintained even if webview loses focus + if (this.debugState.remoteDebuggingEnabled) { + // check few outof bound issue + if ( + this.debugConfig.lambdaTimeout && + (this.debugConfig.lambdaTimeout > 900 || this.debugConfig.lambdaTimeout < 0) + ) { + this.debugConfig.lambdaTimeout = 900 + } + if ( + this.debugConfig.debugPort && + (this.debugConfig.debugPort > 65535 || this.debugConfig.debugPort <= 0) + ) { + this.debugConfig.debugPort = 9229 + } + + // acquire invoke lock + if (this.debugState.remoteDebuggingEnabled && !(await client.checkReadyToInvoke())) { + return + } + + if (!this.debugState.isDebugging) { + this.debugState.isDebugging = await client.startDebugging({ + functionArn: this.initialData.FunctionArn, + functionName: this.initialData.FunctionName, + port: this.debugConfig.debugPort ?? 9229, + sourceMap: this.runtimeSettings.sourceMapEnabled, + localRoot: this.debugConfig.localRootPath, + shouldPublishVersion: this.debugConfig.shouldPublishVersion, + remoteRoot: + this.debugConfig.remoteRootPath !== '' ? this.debugConfig.remoteRootPath : '/var/task', + skipFiles: (this.runtimeSettings.skipFiles !== '' + ? this.runtimeSettings.skipFiles + : '/**' + ).split(','), + justMyCode: this.runtimeSettings.justMyCode, + projectName: this.runtimeSettings.projectName, + otherDebugParams: this.debugConfig.otherDebugParams, + layerArn: this.initialData.remoteDebugLayer, + lambdaTimeout: this.debugConfig.lambdaTimeout ?? 900, + outFiles: this.runtimeSettings.outFiles?.split(','), + }) + if (!this.debugState.isDebugging) { + // user cancel or failed to start debugging + return + } + } + this.debugState.showDebugTimer = false + } + let event = '' - if (this.payload === 'sampleEvents' || this.payload === 'savedEvents') { - event = this.sampleText - } else if (this.payload === 'localFile') { - if (this.selectedFile && this.selectedFilePath) { - const resp = await client.loadFile(this.selectedFilePath) + if (this.uiState.payload === 'sampleEvents' || this.uiState.payload === 'savedEvents') { + event = this.payloadData.sampleText + } else if (this.uiState.payload === 'localFile') { + if (this.payloadData.selectedFile && this.payloadData.selectedFilePath) { + const resp = await client.loadFile(this.payloadData.selectedFilePath) if (resp) { event = resp.sample } } } - await client.invokeLambda(event, this.initialData.Source) + + await client.invokeLambda(event, this.initialData.Source, this.debugState.remoteDebuggingEnabled) + await this.syncStateFromWorkspace() + }, + + async removeDebugSetup() { + this.debugState.isDebugging = await client.stopDebugging() + }, + + async startDebugTimer() { + this.debugState.debugTimeRemaining = await client.getDebugTimeRemaining() + if (this.debugState.debugTimeRemaining <= 0) { + return + } + this.debugState.showDebugTimer = true + this.debugState.debugTimer = window.setInterval(() => { + this.debugState.debugTimeRemaining-- + if (this.debugState.debugTimeRemaining <= 0) { + this.clearDebugTimer() + } + }, 1000) as number | undefined + }, + + clearDebugTimer() { + if (this.debugState.debugTimer) { + window.clearInterval(this.debugState.debugTimer) + this.debugState.debugTimeRemaining = 0 + this.debugState.debugTimer = undefined + this.debugState.showDebugTimer = false + } + }, + + toggleCollapsible() { + this.uiState.isCollapsed = !this.uiState.isCollapsed + }, + + async openHandler() { + await client.tryOpenHandlerFile() + }, + + async openHandlerWithDelay() { + const preValue = this.debugConfig.localRootPath + // user is inputting the dir, only try to open dir if user stopped typing for 1 second + await new Promise((resolve) => setTimeout(resolve, 1000)) + if (preValue !== this.debugConfig.localRootPath) { + return + } + // try open if user stop input for 1 second + await client.tryOpenHandlerFile(this.debugConfig.localRootPath) + this.debugState.handlerFileAvailable = await client.getHandlerAvailable() + }, + + async downloadRemoteCode() { + try { + const path = await client.downloadRemoteCode() + if (path) { + this.debugConfig.localRootPath = path + this.debugState.handlerFileAvailable = await client.getHandlerAvailable() + } + } catch (error) { + console.error('Failed to download remote code:', error) + } }, loadSampleEvent() { @@ -125,7 +406,7 @@ export default defineComponent({ if (!sample) { return } - this.sampleText = JSON.stringify(JSON.parse(sample), undefined, 4) + this.payloadData.sampleText = JSON.stringify(JSON.parse(sample), undefined, 4) }, (e) => { console.error('client.getSamplePayload failed: %s', (e as Error).message) @@ -135,10 +416,9 @@ export default defineComponent({ async loadRemoteTestEvents() { const shouldLoadEvents = - this.payload === 'savedEvents' && + this.uiState.payload === 'savedEvents' && this.initialData.FunctionArn && - this.initialData.FunctionRegion && - !this.initialData.TestEvents + this.initialData.FunctionRegion if (shouldLoadEvents) { this.initialData.TestEvents = await client.listRemoteTestEvents( @@ -148,5 +428,6 @@ export default defineComponent({ } }, }, + mixins: [saveData], }) diff --git a/packages/core/src/shared/clients/lambdaClient.ts b/packages/core/src/shared/clients/lambdaClient.ts index 59af6f314a0..137af843e65 100644 --- a/packages/core/src/shared/clients/lambdaClient.ts +++ b/packages/core/src/shared/clients/lambdaClient.ts @@ -20,16 +20,20 @@ export type LambdaClient = ClassToInterfaceType export class DefaultLambdaClient { private readonly defaultTimeoutInMs: number - public constructor(public readonly regionCode: string) { + public constructor( + public readonly regionCode: string, + public readonly userAgent: string | undefined = undefined + ) { this.defaultTimeoutInMs = 5 * 60 * 1000 // 5 minutes (SDK default is 2 minutes) } - public async deleteFunction(name: string): Promise { + public async deleteFunction(name: string, qualifier?: string): Promise { const sdkClient = await this.createSdkClient() const response = await sdkClient .deleteFunction({ FunctionName: name, + Qualifier: qualifier, }) .promise() @@ -38,7 +42,7 @@ export class DefaultLambdaClient { } } - public async invoke(name: string, payload?: _Blob): Promise { + public async invoke(name: string, payload?: _Blob, version?: string): Promise { const sdkClient = await this.createSdkClient() const response = await sdkClient @@ -46,6 +50,7 @@ export class DefaultLambdaClient { FunctionName: name, LogType: 'Tail', Payload: payload, + Qualifier: version, }) .promise() @@ -158,10 +163,126 @@ export class DefaultLambdaClient { } } + public async updateFunctionConfiguration( + params: Lambda.UpdateFunctionConfigurationRequest, + options: { + maxRetries?: number + initialDelayMs?: number + backoffMultiplier?: number + waitForUpdate?: boolean + } = {} + ): Promise { + const client = await this.createSdkClient() + const maxRetries = options.maxRetries ?? 5 + const initialDelayMs = options.initialDelayMs ?? 1000 + const backoffMultiplier = options.backoffMultiplier ?? 2 + // return until lambda update is completed + const waitForUpdate = options.waitForUpdate ?? false + + let retryCount = 0 + let lastError: any + + // there could be race condition, if function is being updated, wait and retry + while (retryCount <= maxRetries) { + try { + const response = await client.updateFunctionConfiguration(params).promise() + getLogger().debug('updateFunctionConfiguration returned response: %O', response) + if (waitForUpdate) { + // don't return if wait for result + break + } + return response + } catch (e) { + lastError = e + + // Check if this is an "update in progress" error + if (this.isUpdateInProgressError(e) && retryCount < maxRetries) { + const delayMs = initialDelayMs * Math.pow(backoffMultiplier, retryCount) + getLogger().info( + `Update in progress for Lambda function ${params.FunctionName}. ` + + `Retrying in ${delayMs}ms (attempt ${retryCount + 1}/${maxRetries})` + ) + + await new Promise((resolve) => setTimeout(resolve, delayMs)) + retryCount++ + } else { + getLogger().error('Failed to run updateFunctionConfiguration: %s', e) + throw e + } + } + } + + // check if lambda update is completed, use client.getFunctionConfiguration to poll until + // LastUpdateStatus is Successful or Failed + if (waitForUpdate) { + let lastUpdateStatus = 'InProgress' + while (lastUpdateStatus === 'InProgress') { + await new Promise((resolve) => setTimeout(resolve, 1000)) + const response = await client.getFunctionConfiguration({ FunctionName: params.FunctionName }).promise() + lastUpdateStatus = response.LastUpdateStatus ?? 'Failed' + if (lastUpdateStatus === 'Successful') { + return response + } else if (lastUpdateStatus === 'Failed') { + getLogger().error('Failed to update function configuration: %O', response) + throw new Error(`Failed to update function configuration: ${response.LastUpdateStatusReason}`) + } + } + } + + getLogger().error(`Failed to update function configuration after ${maxRetries} retries: %s`, lastError) + throw lastError + } + + public async publishVersion( + name: string, + options: { waitForUpdate?: boolean } = {} + ): Promise { + const client = await this.createSdkClient() + // return until lambda update is completed + const waitForUpdate = options.waitForUpdate ?? false + const response = await client + .publishVersion({ + FunctionName: name, + }) + .promise() + + if (waitForUpdate) { + let state = 'Pending' + while (state === 'Pending') { + await new Promise((resolve) => setTimeout(resolve, 1000)) + const statusResponse = await client + .getFunctionConfiguration({ FunctionName: name, Qualifier: response.Version }) + .promise() + state = statusResponse.State ?? 'Failed' + if (state === 'Active' || state === 'InActive') { + // version creation finished + return statusResponse + } else if (state === 'Failed') { + getLogger().error('Failed to create Version: %O', statusResponse) + throw new Error(`Failed to create Version: ${statusResponse.LastUpdateStatusReason}`) + } + } + } + + return response + } + + private isUpdateInProgressError(error: any): boolean { + return ( + error?.message && + error.message.includes( + 'The operation cannot be performed at this time. An update is in progress for resource:' + ) + ) + } + private async createSdkClient(): Promise { return await globals.sdkClientBuilder.createAwsService( Lambda, - { httpOptions: { timeout: this.defaultTimeoutInMs } }, + { + httpOptions: { timeout: this.defaultTimeoutInMs }, + customUserAgent: this.userAgent, + }, this.regionCode ) } diff --git a/packages/core/src/shared/globalState.ts b/packages/core/src/shared/globalState.ts index 2ec0a328d24..e8e6a3bff44 100644 --- a/packages/core/src/shared/globalState.ts +++ b/packages/core/src/shared/globalState.ts @@ -79,6 +79,8 @@ export type globalKey = | 'aws.toolkit.lambda.walkthroughSelected' | 'aws.toolkit.lambda.walkthroughCompleted' | 'aws.toolkit.appComposer.templateToOpenOnStart' + | 'aws.lambda.remoteDebugContext' + | 'aws.lambda.remoteDebugSnapshot' // List of Domain-Users to show/hide Sagemaker SpaceApps in AWS Explorer. | 'aws.sagemaker.selectedDomainUsers' diff --git a/packages/core/src/shared/telemetry/vscodeTelemetry.json b/packages/core/src/shared/telemetry/vscodeTelemetry.json index 9b29d1a65a0..55f4f8934cf 100644 --- a/packages/core/src/shared/telemetry/vscodeTelemetry.json +++ b/packages/core/src/shared/telemetry/vscodeTelemetry.json @@ -1141,6 +1141,77 @@ { "name": "appbuilder_lambda2sam", "description": "User click Convert a lambda function to SAM project" + }, + { + "name": "lambda_remoteDebugStop", + "description": "user stop remote debugging", + "metadata": [ + { + "type": "sessionDuration", + "required": false + } + ] + }, + { + "name": "lambda_remoteDebugStart", + "description": "user start remote debugging", + "metadata": [ + { + "type": "runtimeString", + "required": false + }, + { + "type": "source", + "required": false + }, + { + "type": "action", + "required": false + } + ] + }, + { + "name": "lambda_remoteDebugPrecheck", + "description": "user click remote debug checkbox", + "metadata": [ + { + "type": "runtimeString", + "required": false + }, + { + "type": "source", + "required": false + }, + { + "type": "action", + "required": false + } + ] + }, + { + "name": "lambda_invokeRemote", + "description": "Called when invoking lambdas remotely", + "metadata": [ + { + "type": "result" + }, + { + "type": "runtime", + "required": false + }, + { + "type": "source", + "required": false + }, + { + "type": "runtimeString", + "required": false + }, + { + "type": "action", + "required": false + } + ] } ] } diff --git a/packages/core/src/shared/utilities/pathFind.ts b/packages/core/src/shared/utilities/pathFind.ts index 04622733a66..a0eea9e38ae 100644 --- a/packages/core/src/shared/utilities/pathFind.ts +++ b/packages/core/src/shared/utilities/pathFind.ts @@ -18,6 +18,7 @@ let vscPath: string let sshPath: string let gitPath: string let bashPath: string +let javaPath: string const pathMap = new Map() /** @@ -145,6 +146,44 @@ export async function findSshPath(useCache: boolean = true): Promise { + if (javaPath !== undefined) { + return javaPath + } + + const paths = [ + 'java', // Try $PATH first + '/usr/bin/java', + '/usr/local/bin/java', + '/opt/java/bin/java', + // Common Oracle JDK locations + '/usr/lib/jvm/default-java/bin/java', + '/usr/lib/jvm/java-11-openjdk/bin/java', + '/usr/lib/jvm/java-8-openjdk/bin/java', + // Windows locations + 'C:/Program Files/Java/jre1.8.0_301/bin/java.exe', + 'C:/Program Files/Java/jdk1.8.0_301/bin/java.exe', + 'C:/Program Files/OpenJDK/openjdk-11.0.2/bin/java.exe', + 'C:/Program Files (x86)/Java/jre1.8.0_301/bin/java.exe', + 'C:/Program Files (x86)/Java/jdk1.8.0_301/bin/java.exe', + // macOS locations + '/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java', + '/usr/libexec/java_home', + ] + for (const p of paths) { + if (!p || ('java' !== p && !(await fs.exists(p)))) { + continue + } + if (await tryRun(p, ['-version'])) { + javaPath = p + return p + } + } +} + /** * Gets the configured `git` path, or falls back to "ssh" (not absolute), * or tries common locations, or returns undefined. diff --git a/packages/core/src/test/lambda/commands/deleteLambda.test.ts b/packages/core/src/test/lambda/commands/deleteLambda.test.ts index 366d7344ef6..8da82c2c21e 100644 --- a/packages/core/src/test/lambda/commands/deleteLambda.test.ts +++ b/packages/core/src/test/lambda/commands/deleteLambda.test.ts @@ -11,7 +11,7 @@ import { stub } from '../../utilities/stubber' describe('deleteLambda', async function () { function createLambdaClient() { - const client = stub(DefaultLambdaClient, { regionCode: 'region-1' }) + const client = stub(DefaultLambdaClient, { regionCode: 'region-1', userAgent: undefined }) client.deleteFunction.resolves() return client diff --git a/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts b/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts index 8cbeedf25f3..ba8d7ccd516 100644 --- a/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts +++ b/packages/core/src/test/lambda/explorer/cloudFormationNodes.test.ts @@ -26,7 +26,7 @@ import { getLabel } from '../../../shared/treeview/utils' const regionCode = 'someregioncode' function createLambdaClient(...functionNames: string[]) { - const client = stub(DefaultLambdaClient, { regionCode }) + const client = stub(DefaultLambdaClient, { regionCode, userAgent: undefined }) client.listFunctions.returns(asyncGenerator(functionNames.map((name) => ({ FunctionName: name })))) return client diff --git a/packages/core/src/test/lambda/explorer/lambdaNodes.test.ts b/packages/core/src/test/lambda/explorer/lambdaNodes.test.ts index 2c94d28ba9b..86ed7bbe44c 100644 --- a/packages/core/src/test/lambda/explorer/lambdaNodes.test.ts +++ b/packages/core/src/test/lambda/explorer/lambdaNodes.test.ts @@ -17,7 +17,7 @@ import { DefaultLambdaClient } from '../../../shared/clients/lambdaClient' const regionCode = 'someregioncode' function createLambdaClient(...functionNames: string[]) { - const client = stub(DefaultLambdaClient, { regionCode }) + const client = stub(DefaultLambdaClient, { regionCode, userAgent: undefined }) client.listFunctions.returns(asyncGenerator(functionNames.map((name) => ({ FunctionName: name })))) return client diff --git a/packages/core/src/test/lambda/remoteDebugging/ldkClient.test.ts b/packages/core/src/test/lambda/remoteDebugging/ldkClient.test.ts new file mode 100644 index 00000000000..91f99aa0409 --- /dev/null +++ b/packages/core/src/test/lambda/remoteDebugging/ldkClient.test.ts @@ -0,0 +1,471 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import sinon from 'sinon' +import { Lambda } from 'aws-sdk' +import { LdkClient, getRegionFromArn, isTunnelInfo } from '../../../lambda/remoteDebugging/ldkClient' +import { LocalProxy } from '../../../lambda/remoteDebugging/localProxy' +import * as utils from '../../../lambda/remoteDebugging/utils' +import * as telemetryUtil from '../../../shared/telemetry/util' +import globals from '../../../shared/extensionGlobals' +import { createMockFunctionConfig, createMockProgress } from './testUtils' + +describe('LdkClient', () => { + let sandbox: sinon.SinonSandbox + let ldkClient: LdkClient + let mockLambdaClient: any + let mockIoTSTClient: any + let mockLocalProxy: any + + beforeEach(() => { + sandbox = sinon.createSandbox() + + // Mock Lambda client + mockLambdaClient = { + getFunction: sandbox.stub(), + updateFunctionConfiguration: sandbox.stub(), + publishVersion: sandbox.stub(), + deleteFunction: sandbox.stub(), + } + sandbox.stub(utils, 'getLambdaClientWithAgent').returns(mockLambdaClient) + + // Mock IoT ST client with proper promise structure + const createPromiseStub = () => sandbox.stub() + mockIoTSTClient = { + listTunnels: sandbox.stub().returns({ promise: createPromiseStub() }), + openTunnel: sandbox.stub().returns({ promise: createPromiseStub() }), + closeTunnel: sandbox.stub().returns({ promise: createPromiseStub() }), + rotateTunnelAccessToken: sandbox.stub().returns({ promise: createPromiseStub() }), + } + sandbox.stub(utils, 'getIoTSTClientWithAgent').resolves(mockIoTSTClient) + + // Mock LocalProxy + mockLocalProxy = { + start: sandbox.stub(), + stop: sandbox.stub(), + } + sandbox.stub(LocalProxy.prototype, 'start').callsFake(mockLocalProxy.start) + sandbox.stub(LocalProxy.prototype, 'stop').callsFake(mockLocalProxy.stop) + + // Mock global state + const stateStorage = new Map() + const mockGlobalState = { + get: (key: string) => stateStorage.get(key), + update: async (key: string, value: any) => { + stateStorage.set(key, value) + return Promise.resolve() + }, + } + sandbox.stub(globals, 'globalState').value(mockGlobalState) + + // Mock telemetry util + sandbox.stub(telemetryUtil, 'getClientId').returns('test-client-id') + ldkClient = LdkClient.instance + ldkClient.dispose() + }) + + afterEach(() => { + sandbox.restore() + }) + + describe('Singleton Pattern', () => { + it('should return the same instance', () => { + const instance1 = LdkClient.instance + const instance2 = LdkClient.instance + assert.strictEqual(instance1, instance2, 'Should return the same singleton instance') + }) + }) + + describe('dispose()', () => { + it('should dispose resources properly', () => { + // Set up a mock local proxy + ;(ldkClient as any).localProxy = mockLocalProxy + + ldkClient.dispose() + + assert(mockLocalProxy.stop.calledOnce, 'Should stop local proxy') + assert.strictEqual((ldkClient as any).localProxy, undefined, 'Should clear local proxy reference') + }) + + it('should clear client caches', () => { + // Add some clients to cache + ;(ldkClient as any).lambdaClientCache.set('us-east-1', mockLambdaClient) + ;(ldkClient as any).lambdaClientCache.set('us-west-2', mockLambdaClient) + + assert.strictEqual((ldkClient as any).lambdaClientCache.size, 2, 'Should have cached clients') + + ldkClient.dispose() + + assert.strictEqual((ldkClient as any).lambdaClientCache.size, 0, 'Should clear Lambda client cache') + }) + }) + + describe('createOrReuseTunnel()', () => { + it('should create new tunnel when none exists', async () => { + mockIoTSTClient.listTunnels().promise.resolves({ tunnelSummaries: [] }) + mockIoTSTClient.openTunnel().promise.resolves({ + tunnelId: 'tunnel-123', + sourceAccessToken: 'source-token', + destinationAccessToken: 'dest-token', + }) + + const result = await ldkClient.createOrReuseTunnel('us-east-1') + + assert(result, 'Should return tunnel info') + assert.strictEqual(result?.tunnelID, 'tunnel-123') + assert.strictEqual(result?.sourceToken, 'source-token') + assert.strictEqual(result?.destinationToken, 'dest-token') + assert(mockIoTSTClient.listTunnels.called, 'Should list existing tunnels') + assert(mockIoTSTClient.openTunnel.called, 'Should create new tunnel') + }) + + it('should reuse existing tunnel with sufficient time remaining', async () => { + const existingTunnel = { + tunnelId: 'existing-tunnel', + description: 'RemoteDebugging+test-client-id', + status: 'OPEN', + createdAt: new Date(Date.now() - 60 * 60 * 1000), // 1 hour ago + } + + mockIoTSTClient.listTunnels().promise.resolves({ tunnelSummaries: [existingTunnel] }) + mockIoTSTClient.rotateTunnelAccessToken().promise.resolves({ + sourceAccessToken: 'rotated-source-token', + destinationAccessToken: 'rotated-dest-token', + }) + + const result = await ldkClient.createOrReuseTunnel('us-east-1') + + assert(result, 'Should return tunnel info') + assert.strictEqual(result?.tunnelID, 'existing-tunnel') + assert.strictEqual(result?.sourceToken, 'rotated-source-token') + assert.strictEqual(result?.destinationToken, 'rotated-dest-token') + }) + + it('should handle tunnel creation errors', async () => { + mockIoTSTClient.listTunnels().promise.resolves({ tunnelSummaries: [] }) + mockIoTSTClient.openTunnel().promise.rejects(new Error('Tunnel creation failed')) + + await assert.rejects( + async () => await ldkClient.createOrReuseTunnel('us-east-1'), + /Error creating\/reusing tunnel/, + 'Should throw error on tunnel creation failure' + ) + }) + }) + + describe('refreshTunnelTokens()', () => { + it('should refresh tunnel tokens successfully', async () => { + mockIoTSTClient.rotateTunnelAccessToken().promise.resolves({ + sourceAccessToken: 'new-source-token', + destinationAccessToken: 'new-dest-token', + }) + + const result = await ldkClient.refreshTunnelTokens('tunnel-123', 'us-east-1') + + assert(result, 'Should return tunnel info') + assert.strictEqual(result?.tunnelID, 'tunnel-123') + assert.strictEqual(result?.sourceToken, 'new-source-token') + assert.strictEqual(result?.destinationToken, 'new-dest-token') + }) + + it('should handle token refresh errors', async () => { + mockIoTSTClient.rotateTunnelAccessToken().promise.rejects(new Error('Token refresh failed')) + + await assert.rejects( + async () => await ldkClient.refreshTunnelTokens('tunnel-123', 'us-east-1'), + /Error refreshing tunnel tokens/, + 'Should throw error on token refresh failure' + ) + }) + }) + + describe('getFunctionDetail()', () => { + const mockFunctionConfig: Lambda.FunctionConfiguration = createMockFunctionConfig({ + FunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:testFunction', + }) + + it('should get function details successfully', async () => { + mockLambdaClient.getFunction.resolves({ Configuration: mockFunctionConfig }) + + const result = await ldkClient.getFunctionDetail(mockFunctionConfig.FunctionArn!) + + assert.deepStrictEqual(result, mockFunctionConfig, 'Should return function configuration') + }) + + it('should handle function details retrieval errors', async () => { + mockLambdaClient.getFunction.reset() + mockLambdaClient.getFunction.rejects(new Error('Function not found')) + + const result = await ldkClient.getFunctionDetail(mockFunctionConfig.FunctionArn!) + + assert.strictEqual(result, undefined, 'Should return undefined on error') + }) + + it('should handle invalid ARN', async () => { + const result = await ldkClient.getFunctionDetail('invalid-arn') + + assert.strictEqual(result, undefined, 'Should return undefined for invalid ARN') + }) + }) + + describe('createDebugDeployment()', () => { + const mockFunctionConfig: Lambda.FunctionConfiguration = createMockFunctionConfig({ + FunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:testFunction', + }) + + const mockProgress = createMockProgress() + + beforeEach(() => { + mockLambdaClient.updateFunctionConfiguration.resolves({}) + mockLambdaClient.publishVersion.resolves({ Version: 'v1' }) + }) + + it('should create debug deployment successfully without version publishing', async () => { + const result = await ldkClient.createDebugDeployment( + mockFunctionConfig, + 'dest-token', + 900, + false, + 'layer-arn', + mockProgress as any + ) + + assert.strictEqual(result, '$Latest', 'Should return $Latest for non-version deployment') + assert(mockLambdaClient.updateFunctionConfiguration.calledOnce, 'Should update function configuration') + assert(mockLambdaClient.publishVersion.notCalled, 'Should not publish version') + }) + + it('should create debug deployment with version publishing', async () => { + const result = await ldkClient.createDebugDeployment( + mockFunctionConfig, + 'dest-token', + 900, + true, + 'layer-arn', + mockProgress as any + ) + + assert.strictEqual(result, 'v1', 'Should return version number') + assert(mockLambdaClient.publishVersion.calledOnce, 'Should publish version') + }) + + it('should handle deployment errors', async () => { + mockLambdaClient.updateFunctionConfiguration.reset() + mockLambdaClient.updateFunctionConfiguration.rejects(new Error('Update failed')) + + await assert.rejects( + async () => + await ldkClient.createDebugDeployment( + mockFunctionConfig, + 'dest-token', + 900, + false, + 'layer-arn', + mockProgress as any + ), + /Failed to create debug deployment/, + 'Should throw error on deployment failure' + ) + }) + + it('should handle missing function ARN', async () => { + const configWithoutArn = { ...mockFunctionConfig, FunctionArn: undefined } + + await assert.rejects( + async () => + await ldkClient.createDebugDeployment( + configWithoutArn, + 'dest-token', + 900, + false, + 'layer-arn', + mockProgress as any + ), + /Function ARN is missing/, + 'Should throw error for missing ARN' + ) + }) + }) + + describe('removeDebugDeployment()', () => { + const mockFunctionConfig: Lambda.FunctionConfiguration = createMockFunctionConfig({ + FunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:testFunction', + }) + + beforeEach(() => { + mockLambdaClient.updateFunctionConfiguration.resolves({}) + }) + + it('should remove debug deployment successfully', async () => { + const result = await ldkClient.removeDebugDeployment(mockFunctionConfig, false) + + assert.strictEqual(result, true, 'Should return true on successful removal') + assert(mockLambdaClient.updateFunctionConfiguration.calledOnce, 'Should update function configuration') + }) + + it('should handle removal errors', async () => { + mockLambdaClient.updateFunctionConfiguration.rejects(new Error('Update failed')) + + await assert.rejects( + async () => await ldkClient.removeDebugDeployment(mockFunctionConfig, false), + /Error removing debug deployment/, + 'Should throw error on removal failure' + ) + }) + + it('should handle missing function ARN', async () => { + const configWithoutArn = { ...mockFunctionConfig, FunctionArn: undefined, FunctionName: undefined } + + await assert.rejects( + async () => await ldkClient.removeDebugDeployment(configWithoutArn, false), + /Error removing debug deployment/, + 'Should throw error for missing ARN' + ) + }) + }) + + describe('deleteDebugVersion()', () => { + it('should delete debug version successfully', async () => { + mockLambdaClient.deleteFunction.resolves({}) + + const result = await ldkClient.deleteDebugVersion( + 'arn:aws:lambda:us-east-1:123456789012:function:testFunction', + 'v1' + ) + + assert.strictEqual(result, true, 'Should return true on successful deletion') + assert(mockLambdaClient.deleteFunction.calledOnce, 'Should call deleteFunction') + }) + + it('should handle version deletion errors', async () => { + mockLambdaClient.deleteFunction.rejects(new Error('Delete failed')) + + const result = await ldkClient.deleteDebugVersion( + 'arn:aws:lambda:us-east-1:123456789012:function:testFunction', + 'v1' + ) + + assert.strictEqual(result, false, 'Should return false on deletion error') + }) + + it('should handle invalid ARN for version deletion', async () => { + const result = await ldkClient.deleteDebugVersion('invalid-arn', 'v1') + + assert.strictEqual(result, false, 'Should return false for invalid ARN') + }) + }) + + describe('startProxy()', () => { + beforeEach(() => { + mockLocalProxy.start.resolves(9229) + mockLocalProxy.stop.returns() + }) + + it('should start proxy successfully', async () => { + const result = await ldkClient.startProxy('us-east-1', 'source-token', 9229) + + assert.strictEqual(result, true, 'Should return true on successful start') + assert( + mockLocalProxy.start.calledWith('us-east-1', 'source-token', 9229), + 'Should start proxy with correct parameters' + ) + }) + + it('should stop existing proxy before starting new one', async () => { + // Create a spy for the stop method + const stopSpy = sandbox.spy() + + // Set up existing proxy with the spy + ;(ldkClient as any).localProxy = { stop: stopSpy } + + await ldkClient.startProxy('us-east-1', 'source-token', 9229) + + assert(stopSpy.called, 'Should stop existing proxy') + }) + + it('should handle proxy start errors', async () => { + mockLocalProxy.start.rejects(new Error('Proxy start failed')) + + await assert.rejects( + async () => await ldkClient.startProxy('us-east-1', 'source-token', 9229), + /Failed to start proxy/, + 'Should throw error on proxy start failure' + ) + }) + }) + + describe('stopProxy()', () => { + it('should stop proxy successfully', async () => { + // Set up existing proxy + ;(ldkClient as any).localProxy = { stop: mockLocalProxy.stop } + + const result = await ldkClient.stopProxy() + + assert.strictEqual(result, true, 'Should return true on successful stop') + assert(mockLocalProxy.stop.calledOnce, 'Should stop proxy') + assert.strictEqual((ldkClient as any).localProxy, undefined, 'Should clear proxy reference') + }) + + it('should handle stopping when no proxy exists', async () => { + const result = await ldkClient.stopProxy() + + assert.strictEqual(result, true, 'Should return true when no proxy to stop') + }) + }) +}) + +describe('Helper Functions', () => { + describe('getRegionFromArn', () => { + it('should extract region from valid ARN', () => { + const arn = 'arn:aws:lambda:us-east-1:123456789012:function:testFunction' + const result = getRegionFromArn(arn) + assert.strictEqual(result, 'us-east-1', 'Should extract region correctly') + }) + + it('should handle undefined ARN', () => { + const result = getRegionFromArn(undefined) + assert.strictEqual(result, undefined, 'Should return undefined for undefined ARN') + }) + + it('should handle invalid ARN format', () => { + const result = getRegionFromArn('invalid-arn') + assert.strictEqual(result, undefined, 'Should return undefined for invalid ARN') + }) + + it('should handle ARN with insufficient parts', () => { + const result = getRegionFromArn('arn:aws:lambda') + assert.strictEqual(result, undefined, 'Should return undefined for ARN with insufficient parts') + }) + }) + + describe('isTunnelInfo', () => { + it('should validate correct tunnel info', () => { + const tunnelInfo = { + tunnelID: 'tunnel-123', + sourceToken: 'source-token', + destinationToken: 'dest-token', + } + const result = isTunnelInfo(tunnelInfo) + assert.strictEqual(result, true, 'Should validate correct tunnel info') + }) + + it('should reject invalid tunnel info', () => { + const invalidTunnelInfo = { + tunnelID: 'tunnel-123', + sourceToken: 'source-token', + // missing destinationToken + } + const result = isTunnelInfo(invalidTunnelInfo as any) + assert.strictEqual(result, false, 'Should reject invalid tunnel info') + }) + + it('should reject non-object types', () => { + assert.strictEqual(isTunnelInfo('string' as any), false, 'Should reject string') + assert.strictEqual(isTunnelInfo(123 as any), false, 'Should reject number') + assert.strictEqual(isTunnelInfo(undefined as any), false, 'Should reject undefined') + }) + }) +}) diff --git a/packages/core/src/test/lambda/remoteDebugging/ldkController.test.ts b/packages/core/src/test/lambda/remoteDebugging/ldkController.test.ts new file mode 100644 index 00000000000..6c2a173fdaa --- /dev/null +++ b/packages/core/src/test/lambda/remoteDebugging/ldkController.test.ts @@ -0,0 +1,600 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import * as vscode from 'vscode' +import sinon, { SinonStubbedInstance, createStubInstance } from 'sinon' +import { Lambda } from 'aws-sdk' +import { + RemoteDebugController, + DebugConfig, + activateRemoteDebugging, + revertExistingConfig, + getLambdaSnapshot, +} from '../../../lambda/remoteDebugging/ldkController' +import { LdkClient } from '../../../lambda/remoteDebugging/ldkClient' +import globals from '../../../shared/extensionGlobals' +import * as messages from '../../../shared/utilities/messages' +import { getOpenExternalStub } from '../../globalSetup.test' +import { assertTelemetry } from '../../testUtil' +import { + createMockFunctionConfig, + createMockDebugConfig, + createMockGlobalState, + setupMockLdkClientOperations, + setupMockVSCodeDebugAPIs, + setupMockRevertExistingConfig, + setupDebuggingState, + setupMockCleanupOperations, +} from './testUtils' + +describe('RemoteDebugController', () => { + let sandbox: sinon.SinonSandbox + let mockLdkClient: SinonStubbedInstance + let controller: RemoteDebugController + let mockGlobalState: any + + beforeEach(() => { + sandbox = sinon.createSandbox() + + // Mock LdkClient + mockLdkClient = createStubInstance(LdkClient) + sandbox.stub(LdkClient, 'instance').get(() => mockLdkClient) + + // Mock global state with actual storage + mockGlobalState = createMockGlobalState() + sandbox.stub(globals, 'globalState').value(mockGlobalState) + + // Get controller instance + controller = RemoteDebugController.instance + + // Ensure clean state + controller.ensureCleanState() + }) + + afterEach(() => { + sandbox.restore() + }) + + describe('Singleton Pattern', () => { + it('should return the same instance', () => { + const instance1 = RemoteDebugController.instance + const instance2 = RemoteDebugController.instance + assert.strictEqual(instance1, instance2, 'Should return the same singleton instance') + }) + }) + + describe('State Management', () => { + it('should initialize with clean state', () => { + controller.ensureCleanState() + + assert.strictEqual(controller.isDebugging, false, 'Should not be debugging initially') + assert.strictEqual(controller.qualifier, undefined, 'Qualifier should be undefined initially') + }) + + it('should clean up disposables on ensureCleanState', () => { + // Set up some mock disposables + const mockDisposable = { dispose: sandbox.stub() } + ;(controller as any).debugSessionDisposables.set('test-arn', mockDisposable) + + controller.ensureCleanState() + + assert(mockDisposable.dispose.calledOnce, 'Should dispose existing disposables') + assert.strictEqual((controller as any).debugSessionDisposables.size, 0, 'Should clear disposables map') + }) + }) + + describe('Runtime Support Checks', () => { + it('should support code download for node and python runtimes', () => { + assert.strictEqual(controller.supportCodeDownload('nodejs18.x'), true, 'Should support Node.js') + assert.strictEqual(controller.supportCodeDownload('python3.9'), true, 'Should support Python') + assert.strictEqual( + controller.supportCodeDownload('java11'), + false, + 'Should not support Java for code download' + ) + assert.strictEqual(controller.supportCodeDownload(undefined), false, 'Should not support undefined runtime') + }) + + it('should support remote debug for node, python, and java runtimes', () => { + assert.strictEqual(controller.supportRuntimeRemoteDebug('nodejs18.x'), true, 'Should support Node.js') + assert.strictEqual(controller.supportRuntimeRemoteDebug('python3.9'), true, 'Should support Python') + assert.strictEqual(controller.supportRuntimeRemoteDebug('java11'), true, 'Should support Java') + assert.strictEqual(controller.supportRuntimeRemoteDebug('dotnet6'), false, 'Should not support .NET') + assert.strictEqual( + controller.supportRuntimeRemoteDebug(undefined), + false, + 'Should not support undefined runtime' + ) + }) + + it('should get remote debug layer for supported regions and architectures', () => { + const result = controller.getRemoteDebugLayer('us-east-1', ['x86_64']) + + assert.strictEqual(typeof result, 'string', 'Should return layer ARN for supported region and architecture') + assert(result?.includes('us-east-1'), 'Should contain the region in the ARN') + assert(result?.includes('LDKLayerX86'), 'Should contain the x86 layer name') + }) + + it('should return undefined for unsupported regions', () => { + const result = controller.getRemoteDebugLayer('unsupported-region', ['x86_64']) + + assert.strictEqual(result, undefined, 'Should return undefined for unsupported region') + }) + + it('should return undefined when region or architectures are undefined', () => { + assert.strictEqual(controller.getRemoteDebugLayer(undefined, ['x86_64']), undefined) + assert.strictEqual(controller.getRemoteDebugLayer('us-west-2', undefined), undefined) + }) + }) + + describe('Extension Installation', () => { + it('should return true when extension is already installed', async () => { + // Mock VSCode extensions API - return extension as already installed + const mockExtension = { id: 'ms-vscode.js-debug', isActive: true } + sandbox.stub(vscode.extensions, 'getExtension').returns(mockExtension as any) + + const result = await controller.installDebugExtension('nodejs18.x') + + assert.strictEqual(result, true, 'Should return true when extension is already installed') + }) + + it('should return true when extension installation succeeds', async () => { + // Mock extension as not installed initially, then installed after command + const getExtensionStub = sandbox.stub(vscode.extensions, 'getExtension') + getExtensionStub.onFirstCall().returns(undefined) // Not installed initially + getExtensionStub.onSecondCall().returns({ isActive: true } as any) // Installed after command + + sandbox.stub(vscode.commands, 'executeCommand').resolves() + sandbox.stub(messages, 'showConfirmationMessage').resolves(true) + + const result = await controller.installDebugExtension('python3.9') + + assert.strictEqual(result, true, 'Should return true when installation succeeds') + }) + + it('should return false when user cancels extension installation', async () => { + // Mock extension as not installed + sandbox.stub(vscode.extensions, 'getExtension').returns(undefined) + sandbox.stub(messages, 'showConfirmationMessage').resolves(false) + + const result = await controller.installDebugExtension('python3.9') + + assert.strictEqual(result, false, 'Should return false when user cancels') + }) + + it('should handle Java runtime workflow', async () => { + // Mock extension as already installed to skip extension installation + const mockExtension = { id: 'redhat.java', isActive: true } + sandbox.stub(vscode.extensions, 'getExtension').returns(mockExtension as any) + + // Mock no Java path found + sandbox.stub(require('../../../shared/utilities/pathFind'), 'findJavaPath').resolves(undefined) + + // Mock user choosing to install JVM + const showConfirmationStub = sandbox.stub(messages, 'showConfirmationMessage').resolves(true) + + // Mock openExternal to prevent actual URL opening + // sandbox.stub(vscode.env, 'openExternal').resolves(true) + getOpenExternalStub().resolves(true) + const result = await controller.installDebugExtension('java11') + + assert.strictEqual(result, false, 'Should return false to allow user to install JVM') + assert(showConfirmationStub.calledOnce, 'Should show JVM installation dialog') + }) + + it('should throw error for undefined runtime', async () => { + await assert.rejects( + async () => await controller.installDebugExtension(undefined), + /Runtime is undefined/, + 'Should throw error for undefined runtime' + ) + }) + }) + + describe('Debug Session Management', () => { + let mockConfig: DebugConfig + let mockFunctionConfig: Lambda.FunctionConfiguration + + beforeEach(() => { + mockConfig = createMockDebugConfig({ + layerArn: 'arn:aws:lambda:us-west-2:123456789012:layer:LDKLayerX86:6', + }) + + mockFunctionConfig = createMockFunctionConfig() + }) + + it('should start debugging successfully', async () => { + // Mock VSCode APIs + setupMockVSCodeDebugAPIs(sandbox) + + // Mock runtime support + sandbox.stub(controller, 'supportRuntimeRemoteDebug').returns(true) + + // Mock successful LdkClient operations + setupMockLdkClientOperations(mockLdkClient, mockFunctionConfig) + + // Mock revertExistingConfig + setupMockRevertExistingConfig(sandbox) + + await controller.startDebugging(mockConfig.functionArn, 'nodejs18.x', mockConfig) + + // Assert state changes + assert.strictEqual(controller.isDebugging, true, 'Should be in debugging state') + // Qualifier is only set for version publishing, not for $LATEST + assert.strictEqual(controller.qualifier, undefined, 'Should not set qualifier for $LATEST') + + // Verify LdkClient calls + assert(mockLdkClient.getFunctionDetail.calledWith(mockConfig.functionArn), 'Should get function details') + assert(mockLdkClient.createOrReuseTunnel.calledOnce, 'Should create tunnel') + assert(mockLdkClient.createDebugDeployment.calledOnce, 'Should create debug deployment') + assert(mockLdkClient.startProxy.calledOnce, 'Should start proxy') + + assertTelemetry('lambda_remoteDebugStart', { + result: 'Succeeded', + source: 'remoteDebug', + action: '{"port":9229,"remoteRoot":"/var/task","skipFiles":[],"shouldPublishVersion":false,"lambdaTimeout":900,"layerArn":"arn:aws:lambda:us-west-2:123456789012:layer:LDKLayerX86:6"}', + runtimeString: 'nodejs18.x', + }) + }) + + it('should handle debugging start failure and cleanup', async () => { + // Mock VSCode APIs + setupMockVSCodeDebugAPIs(sandbox) + + // Mock runtime support + sandbox.stub(controller, 'supportRuntimeRemoteDebug').returns(true) + + // Mock function config retrieval success but tunnel creation failure + setupMockLdkClientOperations(mockLdkClient, mockFunctionConfig) + mockLdkClient.createOrReuseTunnel.rejects(new Error('Tunnel creation failed')) + + // Mock revertExistingConfig + setupMockRevertExistingConfig(sandbox) + + let errorThrown = false + try { + await controller.startDebugging(mockConfig.functionArn, 'nodejs18.x', mockConfig) + } catch (error) { + errorThrown = true + assert(error instanceof Error, 'Should throw an error') + assert( + error.message.includes('Error StartDebugging') || error.message.includes('Tunnel creation failed'), + 'Should throw relevant error' + ) + } + + assert(errorThrown, 'Should have thrown an error') + + // Assert state is cleaned up + assert.strictEqual(controller.isDebugging, false, 'Should not be in debugging state after failure') + assert(mockLdkClient.stopProxy.calledOnce, 'Should attempt cleanup') + }) + + it('should handle version publishing workflow', async () => { + // Mock VSCode APIs + setupMockVSCodeDebugAPIs(sandbox) + + // Mock runtime support + sandbox.stub(controller, 'supportRuntimeRemoteDebug').returns(true) + + const versionConfig = { ...mockConfig, shouldPublishVersion: true } + + // Mock successful LdkClient operations with version publishing + setupMockLdkClientOperations(mockLdkClient, mockFunctionConfig) + mockLdkClient.createDebugDeployment.resolves('v1') + + // Mock revertExistingConfig + setupMockRevertExistingConfig(sandbox) + + await controller.startDebugging(versionConfig.functionArn, 'nodejs18.x', versionConfig) + + assert.strictEqual(controller.isDebugging, true, 'Should be in debugging state') + assert.strictEqual(controller.qualifier, 'v1', 'Should set version qualifier') + // Verify telemetry was emitted with version action + assertTelemetry('lambda_remoteDebugStart', { + result: 'Succeeded', + source: 'remoteDebug', + action: '{"port":9229,"remoteRoot":"/var/task","skipFiles":[],"shouldPublishVersion":true,"lambdaTimeout":900,"layerArn":"arn:aws:lambda:us-west-2:123456789012:layer:LDKLayerX86:6"}', + runtimeString: 'nodejs18.x', + }) + }) + + it('should prevent multiple debugging sessions', async () => { + // Set controller to already debugging + controller.isDebugging = true + + await controller.startDebugging(mockConfig.functionArn, 'nodejs18.x', mockConfig) + + // Should not call LdkClient methods + assert(mockLdkClient.getFunctionDetail.notCalled, 'Should not start new session') + }) + }) + + describe('Stop Debugging', () => { + it('should stop debugging successfully', async () => { + // Mock VSCode APIs + sandbox.stub(vscode.commands, 'executeCommand').resolves() + + // Set up debugging state + await setupDebuggingState(controller, mockGlobalState) + + // Mock successful cleanup operations + setupMockCleanupOperations(mockLdkClient) + + await controller.stopDebugging() + + // Assert state is cleaned up + assert.strictEqual(controller.isDebugging, false, 'Should not be in debugging state') + + // Verify cleanup operations + assert(mockLdkClient.stopProxy.calledOnce, 'Should stop proxy') + assert(mockLdkClient.removeDebugDeployment.calledOnce, 'Should remove debug deployment') + assert(mockLdkClient.deleteDebugVersion.calledOnce, 'Should delete debug version') + assertTelemetry('lambda_remoteDebugStop', { + result: 'Succeeded', + }) + }) + + it('should handle stop debugging when not debugging', async () => { + controller.isDebugging = false + + await controller.stopDebugging() + + // Should complete without error when not debugging + assert.strictEqual(controller.isDebugging, false, 'Should remain not debugging') + }) + + it('should handle cleanup errors gracefully', async () => { + // Mock VSCode APIs + sandbox.stub(vscode.commands, 'executeCommand').resolves() + + controller.isDebugging = true + + const mockFunctionConfig = { + FunctionName: 'testFunction', + FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:testFunction', + } + // Set up the snapshot in mock state + await mockGlobalState.update('aws.lambda.remoteDebugSnapshot', mockFunctionConfig) + + // Mock cleanup failure + mockLdkClient.stopProxy.rejects(new Error('Cleanup failed')) + mockLdkClient.removeDebugDeployment.resolves(true) + + await assert.rejects( + async () => await controller.stopDebugging(), + /error when stopping remote debug/, + 'Should throw error on cleanup failure' + ) + + // State should still be cleaned up + assert.strictEqual(controller.isDebugging, false, 'Should clean up state even on error') + // Verify telemetry was emitted for failure + assertTelemetry('lambda_remoteDebugStop', { + result: 'Failed', + }) + }) + }) + + describe('Snapshot Management', () => { + it('should get lambda snapshot from global state', async () => { + const mockSnapshot = { + FunctionName: 'testFunction', + FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:testFunction', + } + // Set up the snapshot in mock state + await mockGlobalState.update('aws.lambda.remoteDebugSnapshot', mockSnapshot) + + const result = getLambdaSnapshot() + + assert.deepStrictEqual(result, mockSnapshot, 'Should return snapshot from global state') + }) + + it('should return undefined when no snapshot exists', () => { + const result = getLambdaSnapshot() + + assert.strictEqual(result, undefined, 'Should return undefined when no snapshot') + }) + }) + + describe('Telemetry Verification', () => { + let mockConfig: DebugConfig + let mockFunctionConfig: Lambda.FunctionConfiguration + + beforeEach(() => { + mockConfig = createMockDebugConfig({ + layerArn: 'arn:aws:lambda:us-west-2:123456789012:layer:LDKLayerX86:6', + }) + + mockFunctionConfig = createMockFunctionConfig() + }) + + it('should emit lambda_remoteDebugStart telemetry for failed debugging start', async () => { + // Mock VSCode APIs + setupMockVSCodeDebugAPIs(sandbox) + + // Mock runtime support + sandbox.stub(controller, 'supportRuntimeRemoteDebug').returns(true) + + // Mock function config retrieval success but tunnel creation failure + setupMockLdkClientOperations(mockLdkClient, mockFunctionConfig) + mockLdkClient.createOrReuseTunnel.rejects(new Error('Tunnel creation failed')) + + // Mock revertExistingConfig + setupMockRevertExistingConfig(sandbox) + + try { + await controller.startDebugging(mockConfig.functionArn, 'nodejs18.x', mockConfig) + } catch (error) { + // Expected to throw + } + + // Verify telemetry was emitted for failure + assertTelemetry('lambda_remoteDebugStart', { + result: 'Failed', + source: 'remoteDebug', + action: '{"port":9229,"remoteRoot":"/var/task","skipFiles":[],"shouldPublishVersion":false,"lambdaTimeout":900,"layerArn":"arn:aws:lambda:us-west-2:123456789012:layer:LDKLayerX86:6"}', + runtimeString: 'nodejs18.x', + }) + }) + }) +}) + +describe('Module Functions', () => { + let sandbox: sinon.SinonSandbox + let mockGlobalState: any + + beforeEach(() => { + sandbox = sinon.createSandbox() + + // Mock global state with actual storage + mockGlobalState = createMockGlobalState() + sandbox.stub(globals, 'globalState').value(mockGlobalState) + }) + + afterEach(() => { + sandbox.restore() + }) + + describe('activateRemoteDebugging', () => { + it('should activate remote debugging and ensure clean state', async () => { + // Mock revertExistingConfig + sandbox + .stub(require('../../../lambda/remoteDebugging/ldkController'), 'revertExistingConfig') + .resolves(true) + + // Mock controller + const mockController = { + ensureCleanState: sandbox.stub(), + } + sandbox.stub(RemoteDebugController, 'instance').get(() => mockController) + + await activateRemoteDebugging() + + assert(mockController.ensureCleanState.calledOnce, 'Should ensure clean state') + }) + + it('should handle activation errors gracefully', async () => { + // Mock revertExistingConfig to throw error + sandbox + .stub(require('../../../lambda/remoteDebugging/ldkController'), 'revertExistingConfig') + .rejects(new Error('Revert failed')) + + // Should not throw error, just handle gracefully + await activateRemoteDebugging() + + // Test passes if no error is thrown + assert(true, 'Should handle activation errors gracefully') + }) + }) + + describe('revertExistingConfig', () => { + let mockLdkClient: SinonStubbedInstance + + beforeEach(() => { + mockLdkClient = createStubInstance(LdkClient) + sandbox.stub(LdkClient, 'instance').get(() => mockLdkClient) + }) + + it('should return true when no existing config', async () => { + // mockGlobalState.get.returns(undefined) + + const result = await revertExistingConfig() + + assert.strictEqual(result, true, 'Should return true when no config to revert') + }) + + it('should revert existing config successfully', async () => { + const mockSnapshot = { + FunctionName: 'testFunction', + FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:testFunction', + Timeout: 30, + } + const mockCurrentConfig = { + FunctionName: 'testFunction', + FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:testFunction', + Timeout: 900, // Different from snapshot + } + + // Set up the snapshot in mock state + await mockGlobalState.update('aws.lambda.remoteDebugSnapshot', mockSnapshot) + mockLdkClient.getFunctionDetail.resolves(mockCurrentConfig) + mockLdkClient.removeDebugDeployment.resolves(true) + + const showConfirmationStub = sandbox.stub(messages, 'showConfirmationMessage').resolves(true) + const result = await revertExistingConfig() + + assert.strictEqual(result, true, 'Should return true on successful revert') + assert(showConfirmationStub.calledOnce, 'Should show confirmation dialog') + assert(mockLdkClient.removeDebugDeployment.calledWith(mockSnapshot, false), 'Should revert config') + }) + + it('should handle user cancellation of revert', async () => { + const mockSnapshot = { + FunctionName: 'testFunction', + FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:testFunction', + } + const mockCurrentConfig = { + FunctionName: 'testFunction', + FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:testFunction', + Timeout: 900, + } + + // Set up the snapshot in mock state + await mockGlobalState.update('aws.lambda.remoteDebugSnapshot', mockSnapshot) + mockLdkClient.getFunctionDetail.resolves(mockCurrentConfig) + + sandbox.stub(messages, 'showConfirmationMessage').resolves(false) + + const result = await revertExistingConfig() + + assert.strictEqual(result, true, 'Should return true when user cancels') + // Verify snapshot was cleared + assert.strictEqual( + mockGlobalState.get('aws.lambda.remoteDebugSnapshot'), + undefined, + 'Should clear snapshot' + ) + }) + + it('should handle corrupted snapshot gracefully', async () => { + const corruptedSnapshot = { + // Missing FunctionArn and FunctionName + Timeout: 30, + } + + // Set up corrupted snapshot in mock state + await mockGlobalState.update('aws.lambda.remoteDebugSnapshot', corruptedSnapshot) + + const result = await revertExistingConfig() + + assert.strictEqual(result, true, 'Should return true for corrupted snapshot') + // Verify snapshot was cleared + assert.strictEqual( + mockGlobalState.get('aws.lambda.remoteDebugSnapshot'), + undefined, + 'Should clear corrupted snapshot' + ) + }) + + it('should handle revert errors', async () => { + const mockSnapshot = { + FunctionName: 'testFunction', + FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:testFunction', + } + + // Set up the snapshot in mock state + await mockGlobalState.update('aws.lambda.remoteDebugSnapshot', mockSnapshot) + mockLdkClient.getFunctionDetail.rejects(new Error('Failed to get function')) + + await assert.rejects( + async () => await revertExistingConfig(), + /Error in revertExistingConfig/, + 'Should throw error on revert failure' + ) + }) + }) +}) diff --git a/packages/core/src/test/lambda/remoteDebugging/localProxy.test.ts b/packages/core/src/test/lambda/remoteDebugging/localProxy.test.ts new file mode 100644 index 00000000000..7c1bd0479b4 --- /dev/null +++ b/packages/core/src/test/lambda/remoteDebugging/localProxy.test.ts @@ -0,0 +1,421 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import sinon from 'sinon' +import WebSocket from 'ws' +import { LocalProxy } from '../../../lambda/remoteDebugging/localProxy' + +describe('LocalProxy', () => { + let sandbox: sinon.SinonSandbox + let localProxy: LocalProxy + + beforeEach(() => { + sandbox = sinon.createSandbox() + localProxy = new LocalProxy() + }) + + afterEach(() => { + localProxy.stop() + sandbox.restore() + }) + + describe('Constructor', () => { + it('should initialize with default values', () => { + const proxy = new LocalProxy() + assert.strictEqual((proxy as any).isConnected, false, 'Should not be connected initially') + assert.strictEqual((proxy as any).reconnectAttempts, 0, 'Should have zero reconnect attempts') + assert.strictEqual((proxy as any).currentStreamId, 1, 'Should start with stream ID 1') + assert.strictEqual((proxy as any).nextConnectionId, 1, 'Should start with connection ID 1') + }) + }) + + describe('Protobuf Loading', () => { + it('should load protobuf definition successfully', async () => { + const proxy = new LocalProxy() + await (proxy as any).loadProtobufDefinition() + + assert((proxy as any).Message, 'Should load Message type') + assert.strictEqual(typeof (proxy as any).Message, 'object', 'Message should be a protobuf Type object') + assert.strictEqual((proxy as any).Message.constructor.name, 'Type', 'Message should be a protobuf Type') + }) + + it('should not reload protobuf definition if already loaded', async () => { + const proxy = new LocalProxy() + await (proxy as any).loadProtobufDefinition() + const firstMessage = (proxy as any).Message + + await (proxy as any).loadProtobufDefinition() + const secondMessage = (proxy as any).Message + + assert.strictEqual(firstMessage, secondMessage, 'Should not reload protobuf definition') + }) + }) + + describe('TCP Server Management', () => { + it('should close TCP server and connections properly', () => { + const mockSocket = { + removeAllListeners: sandbox.stub(), + destroy: sandbox.stub(), + } + + const mockServer = { + removeAllListeners: sandbox.stub(), + close: sandbox.stub().callsArg(0), + } + + // Set up mock state + ;(localProxy as any).tcpServer = mockServer + ;(localProxy as any).tcpConnections = new Map([[1, { socket: mockSocket }]]) + ;(localProxy as any).closeTcpServer() + + assert(mockSocket.removeAllListeners.called, 'Should remove socket listeners') + assert(mockSocket.destroy.calledOnce, 'Should destroy socket') + assert(mockServer.removeAllListeners.called, 'Should remove server listeners') + assert(mockServer.close.calledOnce, 'Should close server') + }) + }) + + describe('WebSocket Connection Management', () => { + it('should create WebSocket with correct URL and headers', async () => { + const mockWs = { + on: sandbox.stub(), + once: sandbox.stub(), + readyState: WebSocket.OPEN, + removeAllListeners: sandbox.stub(), + close: sandbox.stub(), + terminate: sandbox.stub(), + } + + // Set up LocalProxy with required properties + ;(localProxy as any).region = 'us-east-1' + ;(localProxy as any).accessToken = 'test-access-token' + + // Mock the WebSocket constructor + const WebSocketStub = sandbox.stub().returns(mockWs) + sandbox.stub(WebSocket, 'WebSocket').callsFake(WebSocketStub) + + // Mock the open event to resolve the promise + mockWs.on.withArgs('open').callsArg(1) + + await (localProxy as any).connectWebSocket() + + assert(WebSocketStub.calledOnce, 'Should create WebSocket') + const [url, protocols, options] = WebSocketStub.getCall(0).args + + assert(url.includes('wss://data.tunneling.iot.'), 'Should use correct WebSocket URL') + assert(url.includes('.amazonaws.com:443/tunnel'), 'Should use correct WebSocket URL') + assert(url.includes('local-proxy-mode=source'), 'Should set local proxy mode') + assert.deepStrictEqual(protocols, ['aws.iot.securetunneling-3.0'], 'Should use correct protocol') + assert(options && options.headers && options.headers['access-token'], 'Should include access token header') + assert(options && options.headers && options.headers['client-token'], 'Should include client token header') + }) + + it('should handle WebSocket connection errors', async () => { + const mockWs = { + on: sandbox.stub(), + once: sandbox.stub(), + readyState: WebSocket.CONNECTING, + removeAllListeners: sandbox.stub(), + close: sandbox.stub(), + terminate: sandbox.stub(), + } + + sandbox.stub(WebSocket, 'WebSocket').returns(mockWs) + + // Mock the error event + mockWs.on.withArgs('error').callsArgWith(1, new Error('Connection failed')) + + await assert.rejects( + async () => await (localProxy as any).connectWebSocket(), + /Connection failed/, + 'Should throw error on WebSocket connection failure' + ) + }) + + it('should close WebSocket connection properly', () => { + const mockWs = { + readyState: WebSocket.OPEN, + removeAllListeners: sandbox.stub(), + close: sandbox.stub(), + terminate: sandbox.stub(), + once: sandbox.stub(), + } + + ;(localProxy as any).ws = mockWs + ;(localProxy as any).closeWebSocket() + + assert(mockWs.removeAllListeners.called, 'Should remove all listeners') + assert(mockWs.close.calledWith(1000, 'Normal Closure'), 'Should close with normal closure code') + }) + + it('should terminate WebSocket if not open', () => { + const mockWs = { + readyState: WebSocket.CONNECTING, + removeAllListeners: sandbox.stub(), + close: sandbox.stub(), + terminate: sandbox.stub(), + } + + ;(localProxy as any).ws = mockWs + ;(localProxy as any).closeWebSocket() + + assert(mockWs.terminate.calledOnce, 'Should terminate WebSocket if not open') + }) + }) + + describe('Ping/Pong Management', () => { + it('should start ping interval', () => { + const setIntervalStub = sandbox.stub(global, 'setInterval').returns({} as any) + + ;(localProxy as any).startPingInterval() + + assert(setIntervalStub.calledOnce, 'Should start ping interval') + assert.strictEqual(setIntervalStub.getCall(0).args[1], 30000, 'Should ping every 30 seconds') + }) + + it('should stop ping interval', () => { + const clearIntervalStub = sandbox.stub(global, 'clearInterval') + const mockInterval = {} as any + ;(localProxy as any).pingInterval = mockInterval + ;(localProxy as any).stopPingInterval() + + assert(clearIntervalStub.calledWith(mockInterval), 'Should clear ping interval') + assert.strictEqual((localProxy as any).pingInterval, undefined, 'Should clear interval reference') + }) + + it('should send ping when WebSocket is open', () => { + const mockWs = { + readyState: WebSocket.OPEN, + ping: sandbox.stub(), + } + + ;(localProxy as any).ws = mockWs + + // Simulate ping interval callback + const setIntervalStub = sandbox.stub(global, 'setInterval') + ;(localProxy as any).startPingInterval() + + const pingCallback = setIntervalStub.getCall(0).args[0] + pingCallback() + + assert(mockWs.ping.calledOnce, 'Should send ping') + }) + }) + + describe('Message Processing', () => { + beforeEach(async () => { + // Load protobuf definition + await (localProxy as any).loadProtobufDefinition() + }) + + it('should process binary WebSocket messages', () => { + const processMessageStub = sandbox.stub(localProxy as any, 'processMessage') + + // Create a mock message buffer with length prefix + const messageData = Buffer.from('test message') + const buffer = Buffer.alloc(2 + messageData.length) + buffer.writeUInt16BE(messageData.length, 0) + messageData.copy(buffer, 2) + ;(localProxy as any).handleWebSocketMessage(buffer) + + assert(processMessageStub.calledOnce, 'Should process message') + assert(processMessageStub.calledWith(messageData), 'Should pass correct message data') + }) + + it('should handle incomplete message data', () => { + const processMessageStub = sandbox.stub(localProxy as any, 'processMessage') + + // Create incomplete buffer (only length prefix) + const buffer = Buffer.alloc(2) + buffer.writeUInt16BE(100, 0) // Claims 100 bytes but buffer is only 2 + ;(localProxy as any).handleWebSocketMessage(buffer) + + assert(processMessageStub.notCalled, 'Should not process incomplete message') + }) + + it('should handle non-buffer WebSocket messages', () => { + const processMessageStub = sandbox.stub(localProxy as any, 'processMessage') + + ;(localProxy as any).handleWebSocketMessage('string message') + + assert(processMessageStub.notCalled, 'Should not process non-buffer messages') + }) + }) + + describe('TCP Connection Handling', () => { + beforeEach(() => { + ;(localProxy as any).isConnected = true + ;(localProxy as any).isDisposed = false + }) + + it('should handle new TCP connections when connected', () => { + const mockSocket = { + on: sandbox.stub(), + destroy: sandbox.stub(), + once: sandbox.stub(), + } + + const sendStreamStartStub = sandbox.stub(localProxy as any, 'sendStreamStart') + + ;(localProxy as any).handleNewTcpConnection(mockSocket) + + assert(mockSocket.on.calledWith('data'), 'Should listen for data events') + assert(mockSocket.on.calledWith('error'), 'Should listen for error events') + assert(mockSocket.on.calledWith('close'), 'Should listen for close events') + assert(sendStreamStartStub.calledOnce, 'Should send stream start for first connection') + }) + + it('should reject TCP connections when not connected', () => { + ;(localProxy as any).isConnected = false + + const mockSocket = { + destroy: sandbox.stub(), + } + + ;(localProxy as any).handleNewTcpConnection(mockSocket) + + assert(mockSocket.destroy.calledOnce, 'Should destroy socket when not connected') + }) + + it('should reject TCP connections when disposed', () => { + ;(localProxy as any).isDisposed = true + + const mockSocket = { + destroy: sandbox.stub(), + } + + ;(localProxy as any).handleNewTcpConnection(mockSocket) + + assert(mockSocket.destroy.calledOnce, 'Should destroy socket when disposed') + }) + + it('should send connection start for subsequent connections', () => { + ;(localProxy as any).nextConnectionId = 2 // Second connection + + const mockSocket = { + on: sandbox.stub(), + destroy: sandbox.stub(), + once: sandbox.stub(), + } + + const sendConnectionStartStub = sandbox.stub(localProxy as any, 'sendConnectionStart') + + ;(localProxy as any).handleNewTcpConnection(mockSocket) + + assert(sendConnectionStartStub.calledOnce, 'Should send connection start for subsequent connections') + }) + }) + + describe('Lifecycle Management', () => { + it('should start proxy successfully', async () => { + const startTcpServerStub = sandbox.stub(localProxy as any, 'startTcpServer').resolves(9229) + const connectWebSocketStub = sandbox.stub(localProxy as any, 'connectWebSocket').resolves() + + const port = await localProxy.start('us-east-1', 'source-token', 9229) + + assert.strictEqual(port, 9229, 'Should return assigned port') + assert(startTcpServerStub.calledWith(9229), 'Should start TCP server') + assert(connectWebSocketStub.calledOnce, 'Should connect WebSocket') + assert.strictEqual((localProxy as any).region, 'us-east-1', 'Should store region') + assert.strictEqual((localProxy as any).accessToken, 'source-token', 'Should store access token') + }) + + it('should handle start errors and cleanup', async () => { + sandbox.stub(localProxy as any, 'startTcpServer').resolves(9229) + sandbox.stub(localProxy as any, 'connectWebSocket').rejects(new Error('WebSocket failed')) + const stopStub = sandbox.stub(localProxy, 'stop') + + await assert.rejects( + async () => await localProxy.start('us-east-1', 'source-token', 9229), + /WebSocket failed/, + 'Should throw error on start failure' + ) + + assert(stopStub.calledOnce, 'Should cleanup on start failure') + }) + + it('should stop proxy and cleanup resources', () => { + const stopPingIntervalStub = sandbox.stub(localProxy as any, 'stopPingInterval') + const closeWebSocketStub = sandbox.stub(localProxy as any, 'closeWebSocket') + const closeTcpServerStub = sandbox.stub(localProxy as any, 'closeTcpServer') + + // Set up some state + ;(localProxy as any).isConnected = true + ;(localProxy as any).reconnectAttempts = 5 + ;(localProxy as any).clientToken = 'test-token' + + localProxy.stop() + + assert(stopPingIntervalStub.calledOnce, 'Should stop ping interval') + assert(closeWebSocketStub.calledOnce, 'Should close WebSocket') + assert(closeTcpServerStub.calledOnce, 'Should close TCP server') + assert.strictEqual((localProxy as any).isConnected, false, 'Should reset connection state') + assert.strictEqual((localProxy as any).reconnectAttempts, 0, 'Should reset reconnect attempts') + assert.strictEqual((localProxy as any).clientToken, '', 'Should clear client token') + assert.strictEqual((localProxy as any).isDisposed, true, 'Should mark as disposed') + }) + + it('should handle duplicate stop calls gracefully', () => { + const stopPingIntervalStub = sandbox.stub(localProxy as any, 'stopPingInterval') + + localProxy.stop() + localProxy.stop() // Second call + + // Should not throw error and should handle gracefully + assert(stopPingIntervalStub.calledOnce, 'Should only stop once') + }) + }) + + describe('Message Sending', () => { + beforeEach(async () => { + await (localProxy as any).loadProtobufDefinition() + }) + + it('should send messages when WebSocket is open', () => { + const mockWs = { + readyState: WebSocket.OPEN, + send: sandbox.stub(), + } + + ;(localProxy as any).ws = mockWs + ;(localProxy as any).serviceId = 'WSS' + ;(localProxy as any).sendMessage(1, 1, 1, Buffer.from('test')) + + assert(mockWs.send.calledOnce, 'Should send message') + const sentData = mockWs.send.getCall(0).args[0] + assert(Buffer.isBuffer(sentData), 'Should send buffer data') + assert(sentData.length > 2, 'Should include length prefix') + }) + + it('should not send messages when WebSocket is not open', () => { + const mockWs = { + readyState: WebSocket.CONNECTING, + send: sandbox.stub(), + } + + ;(localProxy as any).ws = mockWs + ;(localProxy as any).sendMessage(1, 1, 1, Buffer.from('test')) + + assert(mockWs.send.notCalled, 'Should not send when WebSocket is not open') + }) + + it('should split large data into chunks', () => { + const mockWs = { + readyState: WebSocket.OPEN, + send: sandbox.stub(), + } + + ;(localProxy as any).ws = mockWs + + // Create data larger than max chunk size (63KB) + const largeData = Buffer.alloc(70 * 1024, 'a') + + ;(localProxy as any).sendData(1, 1, largeData) + + assert(mockWs.send.calledTwice, 'Should split large data into chunks') + }) + }) +}) diff --git a/packages/core/src/test/lambda/remoteDebugging/testUtils.ts b/packages/core/src/test/lambda/remoteDebugging/testUtils.ts new file mode 100644 index 00000000000..67a53b15d61 --- /dev/null +++ b/packages/core/src/test/lambda/remoteDebugging/testUtils.ts @@ -0,0 +1,177 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import sinon from 'sinon' +import { Lambda } from 'aws-sdk' +import { LambdaFunctionNode } from '../../../lambda/explorer/lambdaFunctionNode' +import { InitialData } from '../../../lambda/vue/remoteInvoke/invokeLambda' +import { DebugConfig } from '../../../lambda/remoteDebugging/ldkController' + +/** + * Creates a mock Lambda function configuration for testing + */ +export function createMockFunctionConfig( + overrides: Partial = {} +): Lambda.FunctionConfiguration { + return { + FunctionName: 'testFunction', + FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:testFunction', + Runtime: 'nodejs18.x', + Handler: 'index.handler', + Timeout: 30, + Layers: [], + Environment: { Variables: {} }, + Architectures: ['x86_64'], + SnapStart: { ApplyOn: 'None' }, + ...overrides, + } +} + +/** + * Creates a mock Lambda function node for testing + */ +export function createMockFunctionNode(overrides: Partial = {}): LambdaFunctionNode { + const config = createMockFunctionConfig() + return { + configuration: config, + regionCode: 'us-west-2', + localDir: '/local/path', + ...overrides, + } as LambdaFunctionNode +} + +/** + * Creates mock initial data for RemoteInvokeWebview testing + */ +export function createMockInitialData(overrides: Partial = {}): InitialData { + const mockFunctionNode = createMockFunctionNode() + return { + FunctionName: 'testFunction', + FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:testFunction', + FunctionRegion: 'us-west-2', + InputSamples: [], + Runtime: 'nodejs18.x', + LocalRootPath: '/local/path', + LambdaFunctionNode: mockFunctionNode, + supportCodeDownload: true, + runtimeSupportsRemoteDebug: true, + regionSupportsRemoteDebug: true, + ...overrides, + } as InitialData +} + +/** + * Creates a mock debug configuration for testing + */ +export function createMockDebugConfig(overrides: Partial = {}): DebugConfig { + return { + functionArn: 'arn:aws:lambda:us-west-2:123456789012:function:testFunction', + functionName: 'testFunction', + port: 9229, + localRoot: '/local/path', + remoteRoot: '/var/task', + skipFiles: [], + shouldPublishVersion: false, + lambdaTimeout: 900, + layerArn: 'arn:aws:lambda:us-west-2:123456789012:layer:LDKLayerX86:6', + ...overrides, + } +} + +/** + * Creates a mock global state for testing + */ +export function createMockGlobalState(): any { + const stateStorage = new Map() + return { + get: (key: string) => stateStorage.get(key), + tryGet: (key: string, type?: any, defaultValue?: any) => { + const value = stateStorage.get(key) + return value !== undefined ? value : defaultValue + }, + update: async (key: string, value: any) => { + stateStorage.set(key, value) + return Promise.resolve() + }, + } +} + +/** + * Sets up common mocks for VSCode APIs + */ +export function setupVSCodeMocks(sandbox: sinon.SinonSandbox) { + return { + startDebugging: sandbox.stub(), + executeCommand: sandbox.stub(), + onDidTerminateDebugSession: sandbox.stub().returns({ dispose: sandbox.stub() }), + } +} + +/** + * Creates a mock progress reporter for testing + */ +export function createMockProgress(): any { + return { + report: sinon.stub(), + } +} + +/** + * Sets up common debugging state for stop debugging tests + */ +export function setupDebuggingState(controller: any, mockGlobalState: any, qualifier: string = 'v1') { + controller.isDebugging = true + controller.qualifier = qualifier + ;(controller as any).lastDebugStartTime = Date.now() - 5000 // 5 seconds ago + + const mockFunctionConfig = { + FunctionName: 'testFunction', + FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:testFunction', + } + + return mockGlobalState.update('aws.lambda.remoteDebugSnapshot', mockFunctionConfig) +} + +/** + * Sets up common mock operations for successful cleanup + */ +export function setupMockCleanupOperations(mockLdkClient: any) { + mockLdkClient.stopProxy.resolves(true) + mockLdkClient.removeDebugDeployment.resolves(true) + mockLdkClient.deleteDebugVersion.resolves(true) +} + +/** + * Sets up common mock operations for LdkClient testing + */ +export function setupMockLdkClientOperations(mockLdkClient: any, mockFunctionConfig: any) { + mockLdkClient.getFunctionDetail.resolves(mockFunctionConfig) + mockLdkClient.createOrReuseTunnel.resolves({ + tunnelID: 'tunnel-123', + sourceToken: 'source-token', + destinationToken: 'dest-token', + }) + mockLdkClient.createDebugDeployment.resolves('$LATEST') + mockLdkClient.startProxy.resolves(true) + mockLdkClient.stopProxy.resolves(true) + mockLdkClient.removeDebugDeployment.resolves(true) + mockLdkClient.deleteDebugVersion.resolves(true) +} + +/** + * Sets up common VSCode debug API mocks + */ +export function setupMockVSCodeDebugAPIs(sandbox: sinon.SinonSandbox) { + sandbox.stub(require('vscode').debug, 'startDebugging').resolves(true) + sandbox.stub(require('vscode').commands, 'executeCommand').resolves() + sandbox.stub(require('vscode').debug, 'onDidTerminateDebugSession').returns({ dispose: sandbox.stub() }) +} + +/** + * Sets up mock for revertExistingConfig function + */ +export function setupMockRevertExistingConfig(sandbox: sinon.SinonSandbox) { + return sandbox.stub(require('../../../lambda/remoteDebugging/ldkController'), 'revertExistingConfig').resolves(true) +} diff --git a/packages/core/src/test/lambda/vue/remoteInvoke/invokeLambda.test.ts b/packages/core/src/test/lambda/vue/remoteInvoke/invokeLambda.test.ts index 2a0fcaa0e0d..1b9f4bfde8e 100644 --- a/packages/core/src/test/lambda/vue/remoteInvoke/invokeLambda.test.ts +++ b/packages/core/src/test/lambda/vue/remoteInvoke/invokeLambda.test.ts @@ -471,7 +471,8 @@ describe('RemoteInvokeWebview', () => { createWebviewPanelArgs[1], `Invoke Lambda ${mockFunctionNode.configuration.FunctionName}` ) - assert.deepStrictEqual(createWebviewPanelArgs[2], { viewColumn: -1 }) + // opens in side panel + assert.deepStrictEqual(createWebviewPanelArgs[2], { viewColumn: vscode.ViewColumn.Beside }) }) }) }) diff --git a/packages/core/src/test/lambda/vue/remoteInvoke/invokeLambdaDebugging.test.ts b/packages/core/src/test/lambda/vue/remoteInvoke/invokeLambdaDebugging.test.ts new file mode 100644 index 00000000000..04cce5f9cef --- /dev/null +++ b/packages/core/src/test/lambda/vue/remoteInvoke/invokeLambdaDebugging.test.ts @@ -0,0 +1,595 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import { RemoteInvokeWebview, InitialData } from '../../../../lambda/vue/remoteInvoke/invokeLambda' +import { LambdaClient, DefaultLambdaClient } from '../../../../shared/clients/lambdaClient' +import * as vscode from 'vscode' +import sinon, { SinonStubbedInstance, createStubInstance } from 'sinon' +import { RemoteDebugController, DebugConfig } from '../../../../lambda/remoteDebugging/ldkController' +import { getTestWindow } from '../../../shared/vscode/window' +import { LambdaFunctionNode } from '../../../../lambda/explorer/lambdaFunctionNode' +import * as downloadLambda from '../../../../lambda/commands/downloadLambda' +import * as uploadLambda from '../../../../lambda/commands/uploadLambda' +import * as appBuilderUtils from '../../../../awsService/appBuilder/utils' +import * as messages from '../../../../shared/utilities/messages' +import globals from '../../../../shared/extensionGlobals' +import fs from '../../../../shared/fs/fs' +import { ToolkitError } from '../../../../shared' +import { createMockDebugConfig } from '../../remoteDebugging/testUtils' + +describe('RemoteInvokeWebview - Debugging Functionality', () => { + let outputChannel: vscode.OutputChannel + let client: SinonStubbedInstance + let remoteInvokeWebview: RemoteInvokeWebview + let data: InitialData + let sandbox: sinon.SinonSandbox + let mockDebugController: SinonStubbedInstance + let mockFunctionNode: LambdaFunctionNode + + beforeEach(() => { + sandbox = sinon.createSandbox() + client = createStubInstance(DefaultLambdaClient) + outputChannel = { + appendLine: sandbox.stub(), + show: sandbox.stub(), + } as unknown as vscode.OutputChannel + + mockFunctionNode = { + configuration: { + FunctionName: 'testFunction', + FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:testFunction', + Handler: 'index.handler', + Runtime: 'nodejs18.x', + SnapStart: { ApplyOn: 'None' }, + }, + regionCode: 'us-west-2', + localDir: '/local/path', + } as LambdaFunctionNode + + data = { + FunctionName: 'testFunction', + FunctionArn: 'arn:aws:lambda:us-west-2:123456789012:function:testFunction', + FunctionRegion: 'us-west-2', + InputSamples: [], + Runtime: 'nodejs18.x', + LocalRootPath: '/local/path', + LambdaFunctionNode: mockFunctionNode, + supportCodeDownload: true, + runtimeSupportsRemoteDebug: true, + regionSupportsRemoteDebug: true, + } as InitialData + + remoteInvokeWebview = new RemoteInvokeWebview(outputChannel, client, data) + + // Mock RemoteDebugController + mockDebugController = createStubInstance(RemoteDebugController) + sandbox.stub(RemoteDebugController, 'instance').get(() => mockDebugController) + + // Set handler file as available by default to avoid timeout issues + ;(remoteInvokeWebview as any).handlerFileAvailable = true + }) + + afterEach(() => { + sandbox.restore() + }) + + describe('Debug Timer Management', () => { + it('should start debug timer and count down', async () => { + remoteInvokeWebview.startDebugTimer() + + // Check initial state + assert.strictEqual(remoteInvokeWebview.getDebugTimeRemaining(), 60) + + // Wait a bit and check if timer is counting down + await new Promise((resolve) => { + setTimeout(() => { + const timeRemaining = remoteInvokeWebview.getDebugTimeRemaining() + assert(timeRemaining < 60 && timeRemaining > 0, 'Timer should be counting down') + remoteInvokeWebview.stopDebugTimer() + resolve() + }, 1100) // Wait slightly more than 1 second + }) + }) + + it('should stop debug timer', () => { + remoteInvokeWebview.startDebugTimer() + assert(remoteInvokeWebview.getDebugTimeRemaining() > 0) + + remoteInvokeWebview.stopDebugTimer() + assert.strictEqual(remoteInvokeWebview.getDebugTimeRemaining(), 0) + }) + + it('should handle timer expiration by stopping debugging', async () => { + const stopDebuggingStub = sandbox.stub(remoteInvokeWebview, 'stopDebugging').resolves(true) + + // Mock a very short timer for testing + sandbox.stub(remoteInvokeWebview, 'startDebugTimer').callsFake(() => { + // Simulate immediate timer expiration + setTimeout(async () => { + await (remoteInvokeWebview as any).handleTimerExpired() + }, 10) + }) + + remoteInvokeWebview.startDebugTimer() + + // Wait for timer to expire + await new Promise((resolve) => setTimeout(resolve, 50)) + + assert(stopDebuggingStub.calledOnce, 'stopDebugging should be called when timer expires') + }) + }) + + describe('Debug State Management', () => { + it('should reset server state correctly', () => { + // Set up some state + remoteInvokeWebview.startDebugTimer() + + // Mock the debugging state + mockDebugController.isDebugging = true + + remoteInvokeWebview.resetServerState() + + assert.strictEqual(remoteInvokeWebview.getDebugTimeRemaining(), 0) + assert.strictEqual(remoteInvokeWebview.isWebViewDebugging(), false) + }) + + it('should check if ready to invoke when not invoking', () => { + const result = remoteInvokeWebview.checkReadyToInvoke() + assert.strictEqual(result, true) + }) + + it('should show warning when invoke is in progress', () => { + // Mock the window.showWarningMessage through getTestWindow + getTestWindow().onDidShowMessage(() => { + // Message handler for warning + }) + + // Set invoking state + ;(remoteInvokeWebview as any).isInvoking = true + + const result = remoteInvokeWebview.checkReadyToInvoke() + + assert.strictEqual(result, false) + // The warning should be shown but we can't easily verify it in this test setup + }) + + it('should return correct debugging states', () => { + mockDebugController.isDebugging = true + assert.strictEqual(remoteInvokeWebview.isLDKDebugging(), true) + + mockDebugController.isDebugging = false + assert.strictEqual(remoteInvokeWebview.isLDKDebugging(), false) + }) + }) + + describe('Debug Configuration and Validation', () => { + let mockConfig: DebugConfig + + beforeEach(() => { + mockConfig = createMockDebugConfig({ + functionArn: data.FunctionArn, + functionName: data.FunctionName, + }) + }) + + it('should check ready to debug with valid config', async () => { + // Ensure handler file is available to avoid confirmation dialog + ;(remoteInvokeWebview as any).handlerFileAvailable = true + + const result = await remoteInvokeWebview.checkReadyToDebug(mockConfig) + assert.strictEqual(result, true) + }) + + it('should return false when LambdaFunctionNode is undefined', async () => { + remoteInvokeWebview = new RemoteInvokeWebview(outputChannel, client, { + ...data, + LambdaFunctionNode: undefined, + }) + + const result = await remoteInvokeWebview.checkReadyToDebug(mockConfig) + assert.strictEqual(result, false) + }) + + it('should show warning when handler file is not available', async () => { + const showConfirmationStub = sandbox.stub(messages, 'showConfirmationMessage').resolves(false) + + // Set handler file as not available + ;(remoteInvokeWebview as any).handlerFileAvailable = false + + const result = await remoteInvokeWebview.checkReadyToDebug(mockConfig) + + assert.strictEqual(result, false) + assert(showConfirmationStub.calledOnce) + }) + + it('should show snapstart warning when publishing version with snapstart enabled', async () => { + const showConfirmationStub = sandbox.stub(messages, 'showConfirmationMessage').resolves(false) + + mockConfig.shouldPublishVersion = true + data.LambdaFunctionNode!.configuration.SnapStart = { ApplyOn: 'PublishedVersions' } + + const result = await remoteInvokeWebview.checkReadyToDebug(mockConfig) + + assert.strictEqual(result, false) + assert(showConfirmationStub.calledOnce) + }) + }) + + describe('Debug Session Management', () => { + let mockConfig: DebugConfig + + beforeEach(() => { + mockConfig = createMockDebugConfig({ + functionArn: data.FunctionArn, + functionName: data.FunctionName, + }) + }) + + it('should start debugging successfully', async () => { + // Ensure handler file is available to avoid confirmation dialog + ;(remoteInvokeWebview as any).handlerFileAvailable = true + + mockDebugController.startDebugging.resolves() + mockDebugController.isDebugging = true + + const result = await remoteInvokeWebview.startDebugging(mockConfig) + + assert.strictEqual(result, true) + assert(mockDebugController.startDebugging.calledOnce) + }) + + it('should call stop debugging', async () => { + mockDebugController.isDebugging = true + mockDebugController.stopDebugging.resolves() + + await remoteInvokeWebview.stopDebugging() + + // The method doesn't return a boolean, it returns void + assert(mockDebugController.stopDebugging.calledOnce) + }) + + it('should handle debug pre-check with existing session', async () => { + const showConfirmationStub = sandbox.stub(messages, 'showConfirmationMessage').resolves(true) + const stopDebuggingStub = sandbox.stub(remoteInvokeWebview, 'stopDebugging').resolves(false) + mockDebugController.isDebugging = true + mockDebugController.installDebugExtension.resolves(true) + + // Mock revertExistingConfig - need to import it properly + const ldkController = require('../../../../lambda/remoteDebugging/ldkController') + const revertStub = sandbox.stub(ldkController, 'revertExistingConfig').resolves(true) + + await remoteInvokeWebview.debugPreCheck() + + assert(showConfirmationStub.calledOnce) + assert(stopDebuggingStub.calledOnce) + assert(mockDebugController.installDebugExtension.calledOnce) + assert(revertStub.calledOnce) + }) + }) + + describe('File Operations and Code Management', () => { + it('should prompt for folder selection', async () => { + const mockUri = vscode.Uri.file('/selected/folder') + getTestWindow().onDidShowDialog((d) => d.selectItem(mockUri)) + + const result = await remoteInvokeWebview.promptFolder() + + assert.strictEqual(result, mockUri.fsPath) + assert.strictEqual(remoteInvokeWebview.getLocalPath(), mockUri.fsPath) + }) + + it('should return undefined when no folder is selected', async () => { + getTestWindow().onDidShowDialog((d) => d.close()) + + const result = await remoteInvokeWebview.promptFolder() + + assert.strictEqual(result, undefined) + }) + + it('should try to open handler file successfully', async () => { + const mockHandlerUri = vscode.Uri.file('/local/path/index.js') + sandbox.stub(appBuilderUtils, 'getLambdaHandlerFile').resolves(mockHandlerUri) + sandbox.stub(fs, 'exists').resolves(true) + sandbox.stub(downloadLambda, 'openLambdaFile').resolves() + + const result = await remoteInvokeWebview.tryOpenHandlerFile('/local/path') + + assert.strictEqual(result, true) + assert.strictEqual(remoteInvokeWebview.getHandlerAvailable(), true) + }) + + it('should handle handler file not found', async () => { + sandbox.stub(appBuilderUtils, 'getLambdaHandlerFile').resolves(undefined) + + // Mock the warning message through getTestWindow + getTestWindow().onDidShowMessage(() => { + // Message handler for warning + }) + + const result = await remoteInvokeWebview.tryOpenHandlerFile('/local/path') + + assert.strictEqual(result, false) + assert.strictEqual(remoteInvokeWebview.getHandlerAvailable(), false) + }) + + it('should download remote code successfully', async () => { + const mockUri = vscode.Uri.file('/downloaded/path') + sandbox.stub(downloadLambda, 'runDownloadLambda').resolves(mockUri) + + // Mock workspace state operations + const mockWorkspaceState = { + get: sandbox.stub().returns({}), + update: sandbox.stub().resolves(), + } + sandbox.stub(globals, 'context').value({ + workspaceState: mockWorkspaceState, + }) + + const result = await remoteInvokeWebview.downloadRemoteCode() + + assert.strictEqual(result, mockUri.fsPath) + assert.strictEqual(data.LocalRootPath, mockUri.fsPath) + }) + + it('should handle download failure', async () => { + sandbox.stub(downloadLambda, 'runDownloadLambda').rejects(new Error('Download failed')) + + await assert.rejects( + async () => await remoteInvokeWebview.downloadRemoteCode(), + /Failed to download remote code/ + ) + }) + }) + + describe('File Watching and Code Synchronization', () => { + it('should setup file watcher when local root path exists', () => { + const createFileSystemWatcherStub = sandbox.stub(vscode.workspace, 'createFileSystemWatcher') + const mockWatcher = { + onDidChange: sandbox.stub(), + onDidCreate: sandbox.stub(), + onDidDelete: sandbox.stub(), + } + createFileSystemWatcherStub.returns(mockWatcher as any) + + // Call the private method through reflection + ;(remoteInvokeWebview as any).setupFileWatcher() + + assert(createFileSystemWatcherStub.calledOnce) + assert(mockWatcher.onDidChange.calledOnce) + assert(mockWatcher.onDidCreate.calledOnce) + assert(mockWatcher.onDidDelete.calledOnce) + }) + + it('should handle file changes and prompt for upload', async () => { + const showConfirmationStub = sandbox.stub(messages, 'showMessage').resolves('Yes') + const runUploadDirectoryStub = sandbox.stub(uploadLambda, 'runUploadDirectory').resolves() + + // Mock file watcher setup + let changeHandler: () => Promise + const mockWatcher = { + onDidChange: (handler: () => Promise) => { + changeHandler = handler + }, + onDidCreate: sandbox.stub(), + onDidDelete: sandbox.stub(), + } + sandbox.stub(vscode.workspace, 'createFileSystemWatcher').returns(mockWatcher as any) + + // Setup file watcher + ;(remoteInvokeWebview as any).setupFileWatcher() + + // Trigger file change + await changeHandler!() + + assert(showConfirmationStub.calledOnce) + assert(runUploadDirectoryStub.calledOnce) + }) + }) + + describe('Lambda Invocation with Debugging', () => { + it('should invoke lambda with remote debugging enabled', async () => { + const mockResponse = { + LogResult: Buffer.from('Debug log').toString('base64'), + Payload: '{"result": "debug success"}', + } + client.invoke.resolves(mockResponse) + mockDebugController.isDebugging = true + mockDebugController.qualifier = 'v1' + + const focusStub = sandbox.stub(vscode.commands, 'executeCommand').resolves() + + await remoteInvokeWebview.invokeLambda('{"test": "input"}', 'test', true) + + assert(client.invoke.calledWith(data.FunctionArn, '{"test": "input"}', 'v1')) + assert(focusStub.calledWith('workbench.action.focusFirstEditorGroup')) + }) + + it('should handle timer management during debugging invocation', async () => { + const mockResponse = { + LogResult: Buffer.from('Debug log').toString('base64'), + Payload: '{"result": "debug success"}', + } + client.invoke.resolves(mockResponse) + mockDebugController.isDebugging = true + + const stopTimerStub = sandbox.stub(remoteInvokeWebview, 'stopDebugTimer') + const startTimerStub = sandbox.stub(remoteInvokeWebview, 'startDebugTimer') + + await remoteInvokeWebview.invokeLambda('{"test": "input"}', 'test', true) + + // Timer should be stopped at least once during invoke + assert(stopTimerStub.calledOnce) + assert(startTimerStub.calledOnce) // Called after invoke + }) + }) + + describe('Dispose and Cleanup', () => { + it('should dispose server and clean up resources', async () => { + // Set up debugging state and disposables + ;(remoteInvokeWebview as any).debugging = true + mockDebugController.isDebugging = true + + // Mock disposables + const mockDisposable = { dispose: sandbox.stub() } + ;(remoteInvokeWebview as any).watcherDisposable = mockDisposable + ;(remoteInvokeWebview as any).fileWatcherDisposable = mockDisposable + + await remoteInvokeWebview.disposeServer() + + assert(mockDisposable.dispose.calledTwice) + assert(mockDebugController.stopDebugging.calledOnce) + }) + + it('should handle dispose when not debugging', async () => { + mockDebugController.isDebugging = false + + const mockDisposable = { dispose: sandbox.stub() } + ;(remoteInvokeWebview as any).watcherDisposable = mockDisposable + + await remoteInvokeWebview.disposeServer() + + assert(mockDisposable.dispose.calledOnce) + }) + }) + + describe('Debug Session Event Handling', () => { + it('should handle debug session termination', async () => { + const resetStateStub = sandbox.stub(remoteInvokeWebview, 'resetServerState') + + // Mock debug session termination event + let terminationHandler: (session: vscode.DebugSession) => Promise + sandbox.stub(vscode.debug, 'onDidTerminateDebugSession').callsFake((handler) => { + terminationHandler = handler + return { dispose: sandbox.stub() } + }) + + // Initialize the webview to set up event handlers + remoteInvokeWebview.init() + + // Simulate debug session termination + const mockSession = { name: 'test-session' } as vscode.DebugSession + await terminationHandler!(mockSession) + + assert(resetStateStub.calledOnce) + }) + }) + + describe('Debugging Flow', () => { + let mockConfig: DebugConfig + + beforeEach(() => { + mockConfig = createMockDebugConfig({ + functionArn: data.FunctionArn, + functionName: data.FunctionName, + }) + + // Mock telemetry to avoid issues + sandbox.stub(require('../../../../shared/telemetry/telemetry'), 'telemetry').value({ + lambda_invokeRemote: { + emit: sandbox.stub(), + }, + }) + }) + + it('should handle complete debugging workflow', async () => { + // Setup mocks for successful debugging + mockDebugController.startDebugging.resolves() + mockDebugController.stopDebugging.resolves() + mockDebugController.isDebugging = false + + // Mock the debugging state change after startDebugging is called + mockDebugController.startDebugging.callsFake(async () => { + mockDebugController.isDebugging = true + return Promise.resolve() + }) + + // 1. Start debugging + const startResult = await remoteInvokeWebview.startDebugging(mockConfig) + assert.strictEqual(startResult, true, 'Debug session should start successfully') + + // Set qualifier for invocation + mockDebugController.qualifier = '$LATEST' + + // 2. Test lambda invocation during debugging + const mockResponse = { + LogResult: Buffer.from('Debug invocation log').toString('base64'), + Payload: '{"debugResult": "success"}', + } + client.invoke.resolves(mockResponse) + + await remoteInvokeWebview.invokeLambda('{"debugInput": "test"}', 'integration-test', true) + + // Verify invocation was called with correct parameters + assert(client.invoke.calledWith(data.FunctionArn, '{"debugInput": "test"}', '$LATEST')) + + // 3. Stop debugging + await remoteInvokeWebview.stopDebugging() + + // Verify cleanup operations were called + assert(mockDebugController.stopDebugging.calledOnce, 'Should stop debugging') + }) + + it('should handle debugging failure gracefully', async () => { + // Setup mock for debugging failure + mockDebugController.startDebugging.rejects(new Error('Debug start failed')) + mockDebugController.isDebugging = false + + // Attempt to start debugging - should throw error + try { + await remoteInvokeWebview.startDebugging(mockConfig) + assert.fail('Expected error to be thrown') + } catch (error) { + assert(error instanceof ToolkitError) + assert(error.message.includes('Failed to start debugging')) + assert(error.cause?.message.includes('Debug start failed')) + } + + assert.strictEqual( + remoteInvokeWebview.isWebViewDebugging(), + false, + 'Webview should not be in debugging state' + ) + }) + + it('should handle version publishing workflow', async () => { + // Setup config for version publishing + const versionConfig = { ...mockConfig, shouldPublishVersion: true } + + // Setup mocks for version publishing + mockDebugController.startDebugging.resolves() + mockDebugController.stopDebugging.resolves() + mockDebugController.isDebugging = false + + // Mock the debugging state change after startDebugging is called + mockDebugController.startDebugging.callsFake(async () => { + mockDebugController.isDebugging = true + mockDebugController.qualifier = 'v1' + return Promise.resolve() + }) + + // Start debugging with version publishing + const startResult = await remoteInvokeWebview.startDebugging(versionConfig) + assert.strictEqual(startResult, true, 'Debug session should start successfully') + + // Test invocation with version qualifier + const mockResponse = { + LogResult: Buffer.from('Version debug log').toString('base64'), + Payload: '{"versionResult": "success"}', + } + client.invoke.resolves(mockResponse) + + await remoteInvokeWebview.invokeLambda('{"versionInput": "test"}', 'version-test', true) + + // Should invoke with version qualifier + assert(client.invoke.calledWith(data.FunctionArn, '{"versionInput": "test"}', 'v1')) + + // Stop debugging + await remoteInvokeWebview.stopDebugging() + + assert(mockDebugController.stopDebugging.calledOnce, 'Should stop debugging') + }) + }) +}) diff --git a/packages/core/src/test/shared/applicationBuilder/explorer/nodes/deployedNode.test.ts b/packages/core/src/test/shared/applicationBuilder/explorer/nodes/deployedNode.test.ts index afe8fb54dda..d8c0178593f 100644 --- a/packages/core/src/test/shared/applicationBuilder/explorer/nodes/deployedNode.test.ts +++ b/packages/core/src/test/shared/applicationBuilder/explorer/nodes/deployedNode.test.ts @@ -147,6 +147,7 @@ describe('generateDeployedNode', () => { regionCode: expectedRegionCode, stackName: expectedStackName, resourceTreeEntity: { + Id: 'MyLambdaFunction', Type: 'AWS::Serverless::Function', }, } @@ -237,6 +238,7 @@ describe('generateDeployedNode', () => { regionCode: expectedRegionCode, stackName: expectedStackName, resourceTreeEntity: { + Id: 'my-project-source-bucket-physical-id', Type: 'AWS::S3::Bucket', }, } @@ -284,6 +286,7 @@ describe('generateDeployedNode', () => { regionCode: expectedRegionCode, stackName: expectedStackName, resourceTreeEntity: { + Id: 'my-project-apigw-physical-id', Type: 'AWS::Serverless::Api', }, } @@ -356,6 +359,7 @@ describe('generateDeployedNode', () => { regionCode: expectedRegionCode, stackName: expectedStackName, resourceTreeEntity: { + Id: 'my-unsupported-resource-physical-id', Type: 'AWS::Serverless::UnsupportType', }, } diff --git a/packages/toolkit/.changes/next-release/Feature-ca5ca54f-d5f4-472e-934e-9fa79d783a98.json b/packages/toolkit/.changes/next-release/Feature-ca5ca54f-d5f4-472e-934e-9fa79d783a98.json new file mode 100644 index 00000000000..ab791cd6caa --- /dev/null +++ b/packages/toolkit/.changes/next-release/Feature-ca5ca54f-d5f4-472e-934e-9fa79d783a98.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "Lambda Remote Debugging: Remote invoke configuration webview now supports attaching a debugger to directly debug your lambda function in the cloud." +} diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index 7cb3dfe49cf..86b32c420cd 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -3093,6 +3093,17 @@ } } }, + { + "command": "aws.lambda.remoteDebugging.clearSnapshot", + "title": "%AWS.command.remoteDebugging.clearSnapshot%", + "category": "%AWS.title%", + "enablement": "isCloud9 || !aws.isWebExtHost", + "cloud9": { + "cn": { + "category": "%AWS.title.cn%" + } + } + }, { "command": "aws.invokeLambda", "title": "%AWS.command.invokeLambda%", From 3f3441943dd9ce4c12b582e5a5ad5938d0029eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=A5=A9=20Flora?= Date: Fri, 11 Jul 2025 17:17:27 -0700 Subject: [PATCH 02/69] fix: update Q profile and customizations on language-servers crash restart --- packages/amazonq/src/lsp/client.ts | 86 ++++-- packages/amazonq/src/lsp/config.ts | 10 +- .../test/unit/amazonq/lsp/client.test.ts | 268 ++++++++++++++++++ .../test/unit/amazonq/lsp/config.test.ts | 148 ++++++++++ .../EditRendering/imageRenderer.test.ts | 2 + packages/core/src/test/lambda/utils.test.ts | 4 + 6 files changed, 500 insertions(+), 18 deletions(-) create mode 100644 packages/amazonq/test/unit/amazonq/lsp/client.test.ts diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index bfa0a718148..570a967d8cb 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -254,6 +254,59 @@ async function initializeAuth(client: LanguageClient): Promise { return auth } +// jscpd:ignore-start +async function initializeLanguageServerConfiguration(client: LanguageClient, context: string = 'startup') { + const logger = getLogger('amazonqLsp') + + if (AuthUtil.instance.isConnectionValid()) { + logger.info(`[${context}] Initializing language server configuration`) +// jscpd:ignore-end + + try { + // Send profile configuration + logger.debug(`[${context}] Sending profile configuration to language server`) + await sendProfileToLsp(client) + logger.debug(`[${context}] Profile configuration sent successfully`) + + // Send customization configuration + logger.debug(`[${context}] Sending customization configuration to language server`) + await pushConfigUpdate(client, { + type: 'customization', + customization: getSelectedCustomization(), + }) + logger.debug(`[${context}] Customization configuration sent successfully`) + + logger.info(`[${context}] Language server configuration completed successfully`) + } catch (error) { + logger.error(`[${context}] Failed to initialize language server configuration: ${error}`) + throw error + } + } else { + logger.warn( + `[${context}] Connection invalid, skipping language server configuration - this will cause authentication failures` + ) + const activeConnection = AuthUtil.instance.auth.activeConnection + const connectionState = activeConnection + ? AuthUtil.instance.auth.getConnectionState(activeConnection) + : 'no-connection' + logger.warn(`[${context}] Connection state: ${connectionState}`) + } +} + +async function sendProfileToLsp(client: LanguageClient) { + const logger = getLogger('amazonqLsp') + const profileArn = AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn + + logger.debug(`Sending profile to LSP: ${profileArn || 'undefined'}`) + + await pushConfigUpdate(client, { + type: 'profile', + profileArn: profileArn, + }) + + logger.debug(`Profile sent to LSP successfully`) +} + async function onLanguageServerReady( extensionContext: vscode.ExtensionContext, auth: AmazonQLspAuth, @@ -296,14 +349,7 @@ async function onLanguageServerReady( // We manually push the cached values the first time since event handlers, which should push, may not have been setup yet. // Execution order is weird and should be fixed in the flare implementation. // TODO: Revisit if we need this if we setup the event handlers properly - if (AuthUtil.instance.isConnectionValid()) { - await sendProfileToLsp(client) - - await pushConfigUpdate(client, { - type: 'customization', - customization: getSelectedCustomization(), - }) - } + await initializeLanguageServerConfiguration(client, 'startup') toDispose.push( inlineManager, @@ -405,13 +451,6 @@ async function onLanguageServerReady( // Set this inside onReady so that it only triggers on subsequent language server starts (not the first) onServerRestartHandler(client, auth) ) - - async function sendProfileToLsp(client: LanguageClient) { - await pushConfigUpdate(client, { - type: 'profile', - profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, - }) - } } /** @@ -431,8 +470,21 @@ function onServerRestartHandler(client: LanguageClient, auth: AmazonQLspAuth) { // TODO: Port this metric override to common definitions telemetry.languageServer_crash.emit({ id: 'AmazonQ' }) - // Need to set the auth token in the again - await auth.refreshConnection(true) + const logger = getLogger('amazonqLsp') + logger.info('[crash-recovery] Language server crash detected, reinitializing authentication') + + try { + // Send bearer token + logger.debug('[crash-recovery] Refreshing connection and sending bearer token') + await auth.refreshConnection(true) + logger.debug('[crash-recovery] Bearer token sent successfully') + + // Send profile and customization configuration + await initializeLanguageServerConfiguration(client, 'crash-recovery') + logger.info('[crash-recovery] Authentication reinitialized successfully') + } catch (error) { + logger.error(`[crash-recovery] Failed to reinitialize after crash: ${error}`) + } }) } diff --git a/packages/amazonq/src/lsp/config.ts b/packages/amazonq/src/lsp/config.ts index 66edc9ff6f1..6b88eb98d21 100644 --- a/packages/amazonq/src/lsp/config.ts +++ b/packages/amazonq/src/lsp/config.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ import * as vscode from 'vscode' -import { DevSettings, getServiceEnvVarConfig, BaseLspInstaller } from 'aws-core-vscode/shared' +import { DevSettings, getServiceEnvVarConfig, BaseLspInstaller, getLogger } from 'aws-core-vscode/shared' import { LanguageClient } from 'vscode-languageclient' import { DidChangeConfigurationNotification, @@ -68,23 +68,31 @@ export function toAmazonQLSPLogLevel(logLevel: vscode.LogLevel): LspLogLevel { * push the given config. */ export async function pushConfigUpdate(client: LanguageClient, config: QConfigs) { + const logger = getLogger('amazonqLsp') + switch (config.type) { case 'profile': + logger.debug(`Pushing profile configuration: ${config.profileArn || 'undefined'}`) await client.sendRequest(updateConfigurationRequestType.method, { section: 'aws.q', settings: { profileArn: config.profileArn }, }) + logger.debug(`Profile configuration pushed successfully`) break case 'customization': + logger.debug(`Pushing customization configuration: ${config.customization || 'undefined'}`) client.sendNotification(DidChangeConfigurationNotification.type.method, { section: 'aws.q', settings: { customization: config.customization }, }) + logger.debug(`Customization configuration pushed successfully`) break case 'logLevel': + logger.debug(`Pushing log level configuration`) client.sendNotification(DidChangeConfigurationNotification.type.method, { section: 'aws.logLevel', }) + logger.debug(`Log level configuration pushed successfully`) break } } diff --git a/packages/amazonq/test/unit/amazonq/lsp/client.test.ts b/packages/amazonq/test/unit/amazonq/lsp/client.test.ts new file mode 100644 index 00000000000..beae6a07742 --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/lsp/client.test.ts @@ -0,0 +1,268 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import sinon from 'sinon' +import { LanguageClient } from 'vscode-languageclient' +import { AuthUtil } from 'aws-core-vscode/codewhisperer' +import { AmazonQLspAuth } from '../../../../src/lsp/auth' + +// These tests verify the behavior of the authentication functions +// Since the actual functions are module-level and use real dependencies, +// we test the expected behavior through mock implementations + +describe('Language Server Client Authentication', function () { + let sandbox: sinon.SinonSandbox + let mockClient: any + let mockAuth: any + let authUtilStub: sinon.SinonStub + let loggerStub: any + let getLoggerStub: sinon.SinonStub + let pushConfigUpdateStub: sinon.SinonStub + + beforeEach(() => { + sandbox = sinon.createSandbox() + + // Mock LanguageClient + mockClient = { + sendRequest: sandbox.stub().resolves(), + sendNotification: sandbox.stub(), + onDidChangeState: sandbox.stub(), + } + + // Mock AmazonQLspAuth + mockAuth = { + refreshConnection: sandbox.stub().resolves(), + } + + // Mock AuthUtil + authUtilStub = sandbox.stub(AuthUtil, 'instance').get(() => ({ + isConnectionValid: sandbox.stub().returns(true), + regionProfileManager: { + activeRegionProfile: { arn: 'test-profile-arn' }, + }, + auth: { + getConnectionState: sandbox.stub().returns('valid'), + activeConnection: { id: 'test-connection' }, + }, + })) + + // Create logger stub + loggerStub = { + info: sandbox.stub(), + debug: sandbox.stub(), + warn: sandbox.stub(), + error: sandbox.stub(), + } + + // Clear all relevant module caches + const sharedModuleId = require.resolve('aws-core-vscode/shared') + const configModuleId = require.resolve('../../../../src/lsp/config') + delete require.cache[sharedModuleId] + delete require.cache[configModuleId] + + // jscpd:ignore-start + // Create getLogger stub + getLoggerStub = sandbox.stub().returns(loggerStub) + + // Create a mock shared module with stubbed getLogger + const mockSharedModule = { + getLogger: getLoggerStub, + } + + // Override the require cache with our mock + require.cache[sharedModuleId] = { + id: sharedModuleId, + filename: sharedModuleId, + loaded: true, + parent: undefined, + children: [], + exports: mockSharedModule, + paths: [], + } as any + // jscpd:ignore-end + + // Mock pushConfigUpdate + pushConfigUpdateStub = sandbox.stub().resolves() + const mockConfigModule = { + pushConfigUpdate: pushConfigUpdateStub, + } + + require.cache[configModuleId] = { + id: configModuleId, + filename: configModuleId, + loaded: true, + parent: undefined, + children: [], + exports: mockConfigModule, + paths: [], + } as any + }) + + afterEach(() => { + sandbox.restore() + }) + + describe('initializeLanguageServerConfiguration behavior', function () { + it('should initialize configuration when connection is valid', async function () { + // Test the expected behavior of the function + const mockInitializeFunction = async (client: LanguageClient, context: string) => { + const { getLogger } = require('aws-core-vscode/shared') + const { pushConfigUpdate } = require('../../../../src/lsp/config') + const logger = getLogger('amazonqLsp') + + if (AuthUtil.instance.isConnectionValid()) { + logger.info(`[${context}] Initializing language server configuration`) + + // Send profile configuration + logger.debug(`[${context}] Sending profile configuration to language server`) + await pushConfigUpdate(client, { + type: 'profile', + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + logger.debug(`[${context}] Profile configuration sent successfully`) + + // Send customization configuration + logger.debug(`[${context}] Sending customization configuration to language server`) + await pushConfigUpdate(client, { + type: 'customization', + customization: 'test-customization', + }) + logger.debug(`[${context}] Customization configuration sent successfully`) + + logger.info(`[${context}] Language server configuration completed successfully`) + } else { + logger.warn(`[${context}] Connection invalid, skipping configuration`) + } + } + + await mockInitializeFunction(mockClient as any, 'startup') + + // Verify logging + assert(loggerStub.info.calledWith('[startup] Initializing language server configuration')) + assert(loggerStub.debug.calledWith('[startup] Sending profile configuration to language server')) + assert(loggerStub.debug.calledWith('[startup] Profile configuration sent successfully')) + assert(loggerStub.debug.calledWith('[startup] Sending customization configuration to language server')) + assert(loggerStub.debug.calledWith('[startup] Customization configuration sent successfully')) + assert(loggerStub.info.calledWith('[startup] Language server configuration completed successfully')) + + // Verify pushConfigUpdate was called twice + assert.strictEqual(pushConfigUpdateStub.callCount, 2) + + // Verify profile configuration + assert( + pushConfigUpdateStub.calledWith(mockClient, { + type: 'profile', + profileArn: 'test-profile-arn', + }) + ) + + // Verify customization configuration + assert( + pushConfigUpdateStub.calledWith(mockClient, { + type: 'customization', + customization: 'test-customization', + }) + ) + }) + + it('should log warning when connection is invalid', async function () { + // Mock invalid connection + authUtilStub.get(() => ({ + isConnectionValid: sandbox.stub().returns(false), + auth: { + getConnectionState: sandbox.stub().returns('invalid'), + activeConnection: { id: 'test-connection' }, + }, + })) + + const mockInitializeFunction = async (client: LanguageClient, context: string) => { + const { getLogger } = require('aws-core-vscode/shared') + const logger = getLogger('amazonqLsp') + + // jscpd:ignore-start + if (AuthUtil.instance.isConnectionValid()) { + // Should not reach here + } else { + logger.warn( + `[${context}] Connection invalid, skipping language server configuration - this will cause authentication failures` + ) + const activeConnection = AuthUtil.instance.auth.activeConnection + const connectionState = activeConnection + ? AuthUtil.instance.auth.getConnectionState(activeConnection) + : 'no-connection' + logger.warn(`[${context}] Connection state: ${connectionState}`) + // jscpd:ignore-end + } + } + + await mockInitializeFunction(mockClient as any, 'crash-recovery') + + // Verify warning logs + assert( + loggerStub.warn.calledWith( + '[crash-recovery] Connection invalid, skipping language server configuration - this will cause authentication failures' + ) + ) + assert(loggerStub.warn.calledWith('[crash-recovery] Connection state: invalid')) + + // Verify pushConfigUpdate was not called + assert.strictEqual(pushConfigUpdateStub.callCount, 0) + }) + }) + + describe('crash recovery handler behavior', function () { + it('should reinitialize authentication after crash', async function () { + const mockCrashHandler = async (client: LanguageClient, auth: AmazonQLspAuth) => { + const { getLogger } = require('aws-core-vscode/shared') + const { pushConfigUpdate } = require('../../../../src/lsp/config') + const logger = getLogger('amazonqLsp') + + logger.info('[crash-recovery] Language server crash detected, reinitializing authentication') + + try { + logger.debug('[crash-recovery] Refreshing connection and sending bearer token') + await auth.refreshConnection(true) + logger.debug('[crash-recovery] Bearer token sent successfully') + + // Mock the configuration initialization + if (AuthUtil.instance.isConnectionValid()) { + await pushConfigUpdate(client, { + type: 'profile', + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + } + + logger.info('[crash-recovery] Authentication reinitialized successfully') + } catch (error) { + logger.error(`[crash-recovery] Failed to reinitialize after crash: ${error}`) + } + } + + await mockCrashHandler(mockClient as any, mockAuth as any) + + // Verify crash recovery logging + assert( + loggerStub.info.calledWith( + '[crash-recovery] Language server crash detected, reinitializing authentication' + ) + ) + assert(loggerStub.debug.calledWith('[crash-recovery] Refreshing connection and sending bearer token')) + assert(loggerStub.debug.calledWith('[crash-recovery] Bearer token sent successfully')) + assert(loggerStub.info.calledWith('[crash-recovery] Authentication reinitialized successfully')) + + // Verify auth.refreshConnection was called + assert(mockAuth.refreshConnection.calledWith(true)) + + // Verify profile configuration was sent + assert( + pushConfigUpdateStub.calledWith(mockClient, { + type: 'profile', + profileArn: 'test-profile-arn', + }) + ) + }) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/lsp/config.test.ts b/packages/amazonq/test/unit/amazonq/lsp/config.test.ts index 69b15d6e311..c31e873e181 100644 --- a/packages/amazonq/test/unit/amazonq/lsp/config.test.ts +++ b/packages/amazonq/test/unit/amazonq/lsp/config.test.ts @@ -77,3 +77,151 @@ describe('getAmazonQLspConfig', () => { delete process.env.__AMAZONQLSP_UI } }) + +describe('pushConfigUpdate', () => { + let sandbox: sinon.SinonSandbox + let mockClient: any + let loggerStub: any + let getLoggerStub: sinon.SinonStub + let pushConfigUpdate: any + + beforeEach(() => { + sandbox = sinon.createSandbox() + + // Mock LanguageClient + mockClient = { + sendRequest: sandbox.stub().resolves(), + sendNotification: sandbox.stub(), + } + + // Create logger stub + loggerStub = { + debug: sandbox.stub(), + } + + // Clear all relevant module caches + const configModuleId = require.resolve('../../../../src/lsp/config') + const sharedModuleId = require.resolve('aws-core-vscode/shared') + delete require.cache[configModuleId] + delete require.cache[sharedModuleId] + + // jscpd:ignore-start + // Create getLogger stub and store reference for test verification + getLoggerStub = sandbox.stub().returns(loggerStub) + + // Create a mock shared module with stubbed getLogger + const mockSharedModule = { + getLogger: getLoggerStub, + } + + // Override the require cache with our mock + require.cache[sharedModuleId] = { + id: sharedModuleId, + filename: sharedModuleId, + loaded: true, + parent: undefined, + children: [], + exports: mockSharedModule, + paths: [], + } as any + + // Now require the module - it should use our mocked getLogger + // jscpd:ignore-end + const configModule = require('../../../../src/lsp/config') + pushConfigUpdate = configModule.pushConfigUpdate + }) + + afterEach(() => { + sandbox.restore() + }) + + it('should send profile configuration with logging', async () => { + const config = { + type: 'profile' as const, + profileArn: 'test-profile-arn', + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging + assert(loggerStub.debug.calledWith('Pushing profile configuration: test-profile-arn')) + assert(loggerStub.debug.calledWith('Profile configuration pushed successfully')) + + // Verify client call + assert(mockClient.sendRequest.calledOnce) + assert( + mockClient.sendRequest.calledWith(sinon.match.string, { + section: 'aws.q', + settings: { profileArn: 'test-profile-arn' }, + }) + ) + }) + + it('should send customization configuration with logging', async () => { + const config = { + type: 'customization' as const, + customization: 'test-customization-arn', + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging + assert(loggerStub.debug.calledWith('Pushing customization configuration: test-customization-arn')) + assert(loggerStub.debug.calledWith('Customization configuration pushed successfully')) + + // Verify client call + assert(mockClient.sendNotification.calledOnce) + assert( + mockClient.sendNotification.calledWith(sinon.match.string, { + section: 'aws.q', + settings: { customization: 'test-customization-arn' }, + }) + ) + }) + + it('should handle undefined profile ARN', async () => { + const config = { + type: 'profile' as const, + profileArn: undefined, + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging with undefined + assert(loggerStub.debug.calledWith('Pushing profile configuration: undefined')) + assert(loggerStub.debug.calledWith('Profile configuration pushed successfully')) + }) + + it('should handle undefined customization ARN', async () => { + const config = { + type: 'customization' as const, + customization: undefined, + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging with undefined + assert(loggerStub.debug.calledWith('Pushing customization configuration: undefined')) + assert(loggerStub.debug.calledWith('Customization configuration pushed successfully')) + }) + + it('should send logLevel configuration with logging', async () => { + const config = { + type: 'logLevel' as const, + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging + assert(loggerStub.debug.calledWith('Pushing log level configuration')) + assert(loggerStub.debug.calledWith('Log level configuration pushed successfully')) + + // Verify client call + assert(mockClient.sendNotification.calledOnce) + assert( + mockClient.sendNotification.calledWith(sinon.match.string, { + section: 'aws.logLevel', + }) + ) + }) +}) diff --git a/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts b/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts index 3160f69fa95..8a625fe3544 100644 --- a/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts +++ b/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts @@ -52,6 +52,7 @@ describe('showEdits', function () { delete require.cache[moduleId] delete require.cache[sharedModuleId] + // jscpd:ignore-start // Create getLogger stub and store reference for test verification getLoggerStub = sandbox.stub().returns(loggerStub) @@ -72,6 +73,7 @@ describe('showEdits', function () { } as any // Now require the module - it should use our mocked getLogger + // jscpd:ignore-end const imageRendererModule = require('../../../../../src/app/inline/EditRendering/imageRenderer') showEdits = imageRendererModule.showEdits diff --git a/packages/core/src/test/lambda/utils.test.ts b/packages/core/src/test/lambda/utils.test.ts index bc430a2e20d..a3eebe043a7 100644 --- a/packages/core/src/test/lambda/utils.test.ts +++ b/packages/core/src/test/lambda/utils.test.ts @@ -119,6 +119,7 @@ describe('lambda utils', function () { describe('setFunctionInfo', function () { let mockLambda: LambdaFunction + // jscpd:ignore-start beforeEach(function () { mockLambda = { name: 'test-function', @@ -130,6 +131,7 @@ describe('lambda utils', function () { afterEach(function () { sinon.restore() }) + // jscpd:ignore-end it('merges with existing data', async function () { const existingData = { lastDeployed: 123456, undeployed: true, sha: 'old-sha', handlerFile: 'index.js' } @@ -153,6 +155,7 @@ describe('lambda utils', function () { describe('compareCodeSha', function () { let mockLambda: LambdaFunction + // jscpd:ignore-start beforeEach(function () { mockLambda = { name: 'test-function', @@ -164,6 +167,7 @@ describe('lambda utils', function () { afterEach(function () { sinon.restore() }) + // jscpd:ignore-end it('returns true when local and remote SHA match', async function () { sinon.stub(fs, 'readFileText').resolves(JSON.stringify({ sha: 'same-sha' })) From da9755ab8383b2fe02b5e6e6bbc09f561c694a9a Mon Sep 17 00:00:00 2001 From: Reed Hamilton <49345456+rhamilt@users.noreply.github.com> Date: Tue, 15 Jul 2025 21:17:34 -0700 Subject: [PATCH 03/69] test(lambda): Remove duplicate code in Lambda tests (#7672) ## Problem Duplicate code linter was finding errors in the `utils` tests for Lambda ## Solution Remove duplicate code (now relying on already existing `mockLambda`) --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/core/src/test/lambda/utils.test.ts | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/packages/core/src/test/lambda/utils.test.ts b/packages/core/src/test/lambda/utils.test.ts index bc430a2e20d..975738edeba 100644 --- a/packages/core/src/test/lambda/utils.test.ts +++ b/packages/core/src/test/lambda/utils.test.ts @@ -13,7 +13,6 @@ import { setFunctionInfo, compareCodeSha, } from '../../lambda/utils' -import { LambdaFunction } from '../../lambda/commands/uploadLambda' import { DefaultLambdaClient } from '../../shared/clients/lambdaClient' import { fs } from '../../shared/fs/fs' import { tempDirPath } from '../../shared/filesystemUtilities' @@ -117,16 +116,6 @@ describe('lambda utils', function () { }) describe('setFunctionInfo', function () { - let mockLambda: LambdaFunction - - beforeEach(function () { - mockLambda = { - name: 'test-function', - region: 'us-east-1', - configuration: { FunctionName: 'test-function' }, - } - }) - afterEach(function () { sinon.restore() }) @@ -151,16 +140,6 @@ describe('lambda utils', function () { }) describe('compareCodeSha', function () { - let mockLambda: LambdaFunction - - beforeEach(function () { - mockLambda = { - name: 'test-function', - region: 'us-east-1', - configuration: { FunctionName: 'test-function' }, - } - }) - afterEach(function () { sinon.restore() }) From 26c5a6be1562ae43e3e4fc4a0c7b0a414a4ce17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=A5=A9=20Flora?= Date: Tue, 15 Jul 2025 21:29:48 -0700 Subject: [PATCH 04/69] fix tests --- packages/core/src/test/lambda/utils.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/test/lambda/utils.test.ts b/packages/core/src/test/lambda/utils.test.ts index 3daff4faa66..a3eebe043a7 100644 --- a/packages/core/src/test/lambda/utils.test.ts +++ b/packages/core/src/test/lambda/utils.test.ts @@ -13,6 +13,7 @@ import { setFunctionInfo, compareCodeSha, } from '../../lambda/utils' +import { LambdaFunction } from '../../lambda/commands/uploadLambda' import { DefaultLambdaClient } from '../../shared/clients/lambdaClient' import { fs } from '../../shared/fs/fs' import { tempDirPath } from '../../shared/filesystemUtilities' From 85f50457b619d1ecd4cfa3fd548f0142a49e9211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=A5=A9=20Flora?= Date: Tue, 15 Jul 2025 21:42:20 -0700 Subject: [PATCH 05/69] Fix lint issues --- packages/amazonq/src/lsp/client.ts | 2 +- packages/amazonq/test/unit/amazonq/lsp/client.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index 400c4dd73e9..37e2309e749 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -258,7 +258,7 @@ async function initializeLanguageServerConfiguration(client: LanguageClient, con if (AuthUtil.instance.isConnectionValid()) { logger.info(`[${context}] Initializing language server configuration`) -// jscpd:ignore-end + // jscpd:ignore-end try { // Send profile configuration diff --git a/packages/amazonq/test/unit/amazonq/lsp/client.test.ts b/packages/amazonq/test/unit/amazonq/lsp/client.test.ts index beae6a07742..7c99c47e0ea 100644 --- a/packages/amazonq/test/unit/amazonq/lsp/client.test.ts +++ b/packages/amazonq/test/unit/amazonq/lsp/client.test.ts @@ -194,7 +194,7 @@ describe('Language Server Client Authentication', function () { ? AuthUtil.instance.auth.getConnectionState(activeConnection) : 'no-connection' logger.warn(`[${context}] Connection state: ${connectionState}`) - // jscpd:ignore-end + // jscpd:ignore-end } } From b39ab4ec3e5e11d237b91c61a4881d177c1e7a09 Mon Sep 17 00:00:00 2001 From: Laxman Reddy <141967714+laileni-aws@users.noreply.github.com> Date: Wed, 16 Jul 2025 09:40:07 -0700 Subject: [PATCH 06/69] feat(sagemaker): Merge sagemaker to master (#7681) ## Notes - feat(sagemaker): Merging Feature/sagemaker-connect-phase-2 to master - Reference PR: https://github.com/aws/aws-toolkit-vscode/pull/7677 --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Co-authored-by: aws-toolkit-automation <43144436+aws-toolkit-automation@users.noreply.github.com> Co-authored-by: Roger Zhang Co-authored-by: Reed Hamilton <49345456+rhamilt@users.noreply.github.com> Co-authored-by: Jacob Chung Co-authored-by: aws-ides-bot Co-authored-by: aws-asolidu Co-authored-by: Newton Der Co-authored-by: Newton Der --- .../src/awsService/sagemaker/activation.ts | 47 +++ .../core/src/awsService/sagemaker/commands.ts | 59 ++-- .../src/awsService/sagemaker/constants.ts | 31 ++ .../awsService/sagemaker/credentialMapping.ts | 12 +- .../detached-server/routes/getSessionAsync.ts | 49 ++- .../sagemaker/detached-server/sessionStore.ts | 3 +- .../sagemaker/detached-server/utils.ts | 15 +- .../sagemaker/explorer/sagemakerParentNode.ts | 19 +- .../sagemaker/explorer/sagemakerSpaceNode.ts | 8 +- .../core/src/awsService/sagemaker/model.ts | 2 +- .../src/awsService/sagemaker/remoteUtils.ts | 5 +- .../src/awsService/sagemaker/uriHandlers.ts | 19 +- .../core/src/awsService/sagemaker/utils.ts | 62 +++- packages/core/src/shared/clients/sagemaker.ts | 82 ++++- packages/core/src/shared/remoteSession.ts | 9 +- packages/core/src/shared/sshConfig.ts | 7 +- .../src/shared/telemetry/vscodeTelemetry.json | 9 + .../sagemaker/credentialMapping.test.ts | 62 +++- .../detached-server/routes/getSession.test.ts | 3 + .../routes/getSessionAsync.test.ts | 63 ++-- .../detached-server/sessionStore.test.ts | 6 +- .../sagemaker/detached-server/utils.test.ts | 15 +- .../explorer/sagemakerParentNode.test.ts | 319 +++++++++--------- .../awsService/sagemaker/remoteUtils.test.ts | 6 +- .../test/awsService/sagemaker/utils.test.ts | 86 ++++- .../shared/clients/sagemakerClient.test.ts | 22 +- .../core/src/test/shared/sshConfig.test.ts | 10 + ...-25f95c85-8b96-4cbd-bc3e-da833340be06.json | 4 + ...-2e0b8ccd-08eb-46e6-947d-27ef5701aca8.json | 4 + ...-3d681d73-4171-44f3-a5ee-50f192a398ca.json | 4 + ...-da7c8255-3962-49eb-b4e6-6091eb788bca.json | 4 + ...-3fa7c727-cb0a-439c-9bfe-34a2a1fa99de.json | 4 + ...-d97769de-7dd4-4fe6-a3ba-c951994e6231.json | 4 + 33 files changed, 734 insertions(+), 320 deletions(-) create mode 100644 packages/core/src/awsService/sagemaker/constants.ts create mode 100644 packages/toolkit/.changes/next-release/Bug Fix-25f95c85-8b96-4cbd-bc3e-da833340be06.json create mode 100644 packages/toolkit/.changes/next-release/Bug Fix-2e0b8ccd-08eb-46e6-947d-27ef5701aca8.json create mode 100644 packages/toolkit/.changes/next-release/Bug Fix-3d681d73-4171-44f3-a5ee-50f192a398ca.json create mode 100644 packages/toolkit/.changes/next-release/Bug Fix-da7c8255-3962-49eb-b4e6-6091eb788bca.json create mode 100644 packages/toolkit/.changes/next-release/Feature-3fa7c727-cb0a-439c-9bfe-34a2a1fa99de.json create mode 100644 packages/toolkit/.changes/next-release/Feature-d97769de-7dd4-4fe6-a3ba-c951994e6231.json diff --git a/packages/core/src/awsService/sagemaker/activation.ts b/packages/core/src/awsService/sagemaker/activation.ts index 49a0244c48e..da8392ebad4 100644 --- a/packages/core/src/awsService/sagemaker/activation.ts +++ b/packages/core/src/awsService/sagemaker/activation.ts @@ -3,18 +3,27 @@ * SPDX-License-Identifier: Apache-2.0 */ +import * as path from 'path' +import * as vscode from 'vscode' import { Commands } from '../../shared/vscode/commands2' import { SagemakerSpaceNode } from './explorer/sagemakerSpaceNode' import { SagemakerParentNode } from './explorer/sagemakerParentNode' import * as uriHandlers from './uriHandlers' import { openRemoteConnect, filterSpaceAppsByDomainUserProfiles, stopSpace } from './commands' +import { updateIdleFile, startMonitoringTerminalActivity, ActivityCheckInterval } from './utils' import { ExtContext } from '../../shared/extensions' import { telemetry } from '../../shared/telemetry/telemetry' +import { isSageMaker, UserActivity } from '../../shared/extensionUtilities' + +let terminalActivityInterval: NodeJS.Timeout | undefined export async function activate(ctx: ExtContext): Promise { ctx.extensionContext.subscriptions.push( uriHandlers.register(ctx), Commands.register('aws.sagemaker.openRemoteConnection', async (node: SagemakerSpaceNode) => { + if (!validateNode(node)) { + return + } await telemetry.sagemaker_openRemoteConnection.run(async () => { await openRemoteConnect(node, ctx.extensionContext) }) @@ -27,9 +36,47 @@ export async function activate(ctx: ExtContext): Promise { }), Commands.register('aws.sagemaker.stopSpace', async (node: SagemakerSpaceNode) => { + if (!validateNode(node)) { + return + } await telemetry.sagemaker_stopSpace.run(async () => { await stopSpace(node, ctx.extensionContext) }) }) ) + + // If running in SageMaker AI Space, track user activity for autoshutdown feature + if (isSageMaker('SMAI')) { + // Use /tmp/ directory so the file is cleared on each reboot to prevent stale timestamps. + const tmpDirectory = '/tmp/' + const idleFilePath = path.join(tmpDirectory, '.sagemaker-last-active-timestamp') + + const userActivity = new UserActivity(ActivityCheckInterval) + userActivity.onUserActivity(() => updateIdleFile(idleFilePath)) + + terminalActivityInterval = startMonitoringTerminalActivity(idleFilePath) + + // Write initial timestamp + await updateIdleFile(idleFilePath) + + ctx.extensionContext.subscriptions.push(userActivity, { + dispose: () => { + if (terminalActivityInterval) { + clearInterval(terminalActivityInterval) + terminalActivityInterval = undefined + } + }, + }) + } +} + +/** + * Checks if a node is undefined and shows a warning message if so. + */ +function validateNode(node: unknown): boolean { + if (!node) { + void vscode.window.showWarningMessage('Space information is being refreshed. Please try again shortly.') + return false + } + return true } diff --git a/packages/core/src/awsService/sagemaker/commands.ts b/packages/core/src/awsService/sagemaker/commands.ts index 22a00a25219..0075d7e5dff 100644 --- a/packages/core/src/awsService/sagemaker/commands.ts +++ b/packages/core/src/awsService/sagemaker/commands.ts @@ -18,6 +18,8 @@ import { ExtContext } from '../../shared/extensions' import { SagemakerClient } from '../../shared/clients/sagemaker' import { ToolkitError } from '../../shared/errors' import { showConfirmationMessage } from '../../shared/utilities/messages' +import { RemoteSessionError } from '../../shared/remoteSession' +import { ConnectFromRemoteWorkspaceMessage, InstanceTypeError } from './constants' const localize = nls.loadMessageBundle() @@ -90,23 +92,21 @@ export async function deeplinkConnect( ) if (isRemoteWorkspace()) { - void vscode.window.showErrorMessage( - 'You are in a remote workspace, skipping deeplink connect. Please open from a local workspace.' - ) + void vscode.window.showErrorMessage(ConnectFromRemoteWorkspaceMessage) return } - const remoteEnv = await prepareDevEnvConnection( - connectionIdentifier, - ctx.extensionContext, - 'sm_dl', - session, - wsUrl, - token, - domain - ) - try { + const remoteEnv = await prepareDevEnvConnection( + connectionIdentifier, + ctx.extensionContext, + 'sm_dl', + session, + wsUrl, + token, + domain + ) + await startVscodeRemote( remoteEnv.SessionProcess, remoteEnv.hostname, @@ -114,10 +114,14 @@ export async function deeplinkConnect( remoteEnv.vscPath, 'sagemaker-user' ) - } catch (err) { + } catch (err: any) { getLogger().error( `sm:OpenRemoteConnect: Unable to connect to target space with arn: ${connectionIdentifier} error: ${err}` ) + + if (![RemoteSessionError.MissingExtension, RemoteSessionError.ExtensionVersionTooLow].includes(err.code)) { + throw err + } } } @@ -156,16 +160,29 @@ export async function stopSpace(node: SagemakerSpaceNode, ctx: vscode.ExtensionC } export async function openRemoteConnect(node: SagemakerSpaceNode, ctx: vscode.ExtensionContext) { + if (isRemoteWorkspace()) { + void vscode.window.showErrorMessage(ConnectFromRemoteWorkspaceMessage) + return + } + if (node.getStatus() === 'Stopped') { const client = new SagemakerClient(node.regionCode) - await client.startSpace(node.spaceApp.SpaceName!, node.spaceApp.DomainId!) - await tryRefreshNode(node) - const appType = node.spaceApp.SpaceSettingsSummary?.AppType - if (!appType) { - throw new ToolkitError('AppType is undefined for the selected space. Cannot start remote connection.') + + try { + await client.startSpace(node.spaceApp.SpaceName!, node.spaceApp.DomainId!) + await tryRefreshNode(node) + const appType = node.spaceApp.SpaceSettingsSummary?.AppType + if (!appType) { + throw new ToolkitError('AppType is undefined for the selected space. Cannot start remote connection.') + } + await client.waitForAppInService(node.spaceApp.DomainId!, node.spaceApp.SpaceName!, appType) + await tryRemoteConnection(node, ctx) + } catch (err: any) { + // Ignore InstanceTypeError since it means the user decided not to use an instanceType with more memory + if (err.code !== InstanceTypeError) { + throw err + } } - await client.waitForAppInService(node.spaceApp.DomainId!, node.spaceApp.SpaceName!, appType) - await tryRemoteConnection(node, ctx) } else if (node.getStatus() === 'Running') { await tryRemoteConnection(node, ctx) } diff --git a/packages/core/src/awsService/sagemaker/constants.ts b/packages/core/src/awsService/sagemaker/constants.ts new file mode 100644 index 00000000000..1fc51a1d20d --- /dev/null +++ b/packages/core/src/awsService/sagemaker/constants.ts @@ -0,0 +1,31 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +export const ConnectFromRemoteWorkspaceMessage = + 'Unable to establish new remote connection. Your last active VS Code window is connected to a remote workspace. To open a new SageMaker Studio connection, select your local VS Code window and try again.' + +export const InstanceTypeError = 'InstanceTypeError' + +export const InstanceTypeMinimum = 'ml.t3.large' + +export const InstanceTypeInsufficientMemory: Record = { + 'ml.t3.medium': 'ml.t3.large', + 'ml.c7i.large': 'ml.c7i.xlarge', + 'ml.c6i.large': 'ml.c6i.xlarge', + 'ml.c6id.large': 'ml.c6id.xlarge', + 'ml.c5.large': 'ml.c5.xlarge', +} + +export const InstanceTypeInsufficientMemoryMessage = ( + spaceName: string, + chosenInstanceType: string, + recommendedInstanceType: string +) => { + return `Unable to create app for [${spaceName}] because instanceType [${chosenInstanceType}] is not supported for remote access enabled spaces. Use instanceType with at least 8 GiB memory. Would you like to start your space with instanceType [${recommendedInstanceType}]?` +} + +export const InstanceTypeNotSelectedMessage = (spaceName: string) => { + return `No instanceType specified for [${spaceName}]. ${InstanceTypeMinimum} is the default instance type, which meets minimum 8 GiB memory requirements for remote access. Continuing will start your space with instanceType [${InstanceTypeMinimum}] and remotely connect.` +} diff --git a/packages/core/src/awsService/sagemaker/credentialMapping.ts b/packages/core/src/awsService/sagemaker/credentialMapping.ts index 205fc5fbad4..60d4e94260e 100644 --- a/packages/core/src/awsService/sagemaker/credentialMapping.ts +++ b/packages/core/src/awsService/sagemaker/credentialMapping.ts @@ -10,9 +10,9 @@ import globals from '../../shared/extensionGlobals' import { ToolkitError } from '../../shared/errors' import { DevSettings } from '../../shared/settings' import { Auth } from '../../auth/auth' -import { parseRegionFromArn } from './utils' import { SpaceMappings, SsmConnectionInfo } from './types' import { getLogger } from '../../shared/logger/logger' +import { parseArn } from './detached-server/utils' const mappingFileName = '.sagemaker-space-profiles' const mappingFilePath = path.join(os.homedir(), '.aws', mappingFileName) @@ -81,9 +81,14 @@ export async function persistSSMConnection( wsUrl?: string, token?: string ): Promise { - const region = parseRegionFromArn(appArn) + const { region } = parseArn(appArn) const endpoint = DevSettings.instance.get('endpoints', {})['sagemaker'] ?? '' + // TODO: Hardcoded to 'jupyterlab' due to a bug in Studio that only supports refreshing + // the token for both CodeEditor and JupyterLab Apps in the jupyterlab subdomain. + // This will be fixed shortly after NYSummit launch to support refresh URL in CodeEditor subdomain. + const appSubDomain = 'jupyterlab' + let envSubdomain: string if (endpoint.includes('beta')) { @@ -101,8 +106,7 @@ export async function persistSSMConnection( ? `studio.${region}.sagemaker.aws` : `${envSubdomain}.studio.${region}.asfiovnxocqpcry.com` - const refreshUrl = `https://studio-${domain}.${baseDomain}/api/remoteaccess/token` - + const refreshUrl = `https://studio-${domain}.${baseDomain}/${appSubDomain}` await setSpaceCredentials(appArn, refreshUrl, { sessionId: session ?? '-', url: wsUrl ?? '-', diff --git a/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts b/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts index 32c7c876945..e59b1b9dd10 100644 --- a/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts +++ b/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts @@ -8,6 +8,7 @@ import { IncomingMessage, ServerResponse } from 'http' import url from 'url' import { SessionStore } from '../sessionStore' +import { open, parseArn, readServerInfo } from '../utils' export async function handleGetSessionAsync(req: IncomingMessage, res: ServerResponse): Promise { const parsedUrl = url.parse(req.url || '', true) @@ -37,38 +38,30 @@ export async function handleGetSessionAsync(req: IncomingMessage, res: ServerRes }) ) return - } else { - res.writeHead(200, { 'Content-Type': 'text/plain' }) - res.end( - `No session found for connection identifier: ${connectionIdentifier}. Reconnecting for deeplink is not supported yet.` - ) - return } - // Temporarily disabling reconnect logic for the 7/3 Phase 1 launch. - // Will re-enable in the next release around 7/14. - - // const status = await store.getStatus(connectionIdentifier, requestId) - // if (status === 'pending') { - // res.writeHead(204) - // res.end() - // return - // } else if (status === 'not-started') { - // const serverInfo = await readServerInfo() - // const refreshUrl = await store.getRefreshUrl(connectionIdentifier) + const status = await store.getStatus(connectionIdentifier, requestId) + if (status === 'pending') { + res.writeHead(204) + res.end() + return + } else if (status === 'not-started') { + const serverInfo = await readServerInfo() + const refreshUrl = await store.getRefreshUrl(connectionIdentifier) + const { spaceName } = parseArn(connectionIdentifier) - // const url = `${refreshUrl}?connection_identifier=${encodeURIComponent( - // connectionIdentifier - // )}&request_id=${encodeURIComponent(requestId)}&call_back_url=${encodeURIComponent( - // `http://localhost:${serverInfo.port}/refresh_token` - // )}` + const url = `${refreshUrl}/${encodeURIComponent(spaceName)}?reconnect_identifier=${encodeURIComponent( + connectionIdentifier + )}&reconnect_request_id=${encodeURIComponent(requestId)}&reconnect_callback_url=${encodeURIComponent( + `http://localhost:${serverInfo.port}/refresh_token` + )}` - // await open(url) - // res.writeHead(202, { 'Content-Type': 'text/plain' }) - // res.end('Session is not ready yet. Please retry in a few seconds.') - // await store.markPending(connectionIdentifier, requestId) - // return - // } + await open(url) + res.writeHead(202, { 'Content-Type': 'text/plain' }) + res.end('Session is not ready yet. Please retry in a few seconds.') + await store.markPending(connectionIdentifier, requestId) + return + } } catch (err) { console.error('Error handling session async request:', err) res.writeHead(500, { 'Content-Type': 'text/plain' }) diff --git a/packages/core/src/awsService/sagemaker/detached-server/sessionStore.ts b/packages/core/src/awsService/sagemaker/detached-server/sessionStore.ts index 312765de263..04098f68c89 100644 --- a/packages/core/src/awsService/sagemaker/detached-server/sessionStore.ts +++ b/packages/core/src/awsService/sagemaker/detached-server/sessionStore.ts @@ -49,7 +49,8 @@ export class SessionStore { const asyncEntry = requests[requestId] if (asyncEntry?.status === 'fresh') { - await this.markConsumed(connectionId, requestId) + delete requests[requestId] + await writeMapping(mapping) return asyncEntry } diff --git a/packages/core/src/awsService/sagemaker/detached-server/utils.ts b/packages/core/src/awsService/sagemaker/detached-server/utils.ts index 50e80e536f3..9ac6b6b0303 100644 --- a/packages/core/src/awsService/sagemaker/detached-server/utils.ts +++ b/packages/core/src/awsService/sagemaker/detached-server/utils.ts @@ -44,18 +44,18 @@ export async function readServerInfo(): Promise { } /** - * Parses a SageMaker ARN to extract region and account ID. + * Parses a SageMaker ARN to extract region, account ID, and space name. * Supports formats like: - * arn:aws:sagemaker:::space/ + * arn:aws:sagemaker:::space// * or sm_lc_arn:aws:sagemaker:::space__d-xxxx__ * * If the input is prefixed with an identifier (e.g. "sagemaker-user@"), the function will strip it. * * @param arn - The full SageMaker ARN string - * @returns An object containing the region and accountId + * @returns An object containing the region, accountId, and spaceName * @throws If the ARN format is invalid */ -export function parseArn(arn: string): { region: string; accountId: string } { +export function parseArn(arn: string): { region: string; accountId: string; spaceName: string } { const cleanedArn = arn.includes('@') ? arn.split('@')[1] : arn const regex = /^arn:aws:sagemaker:(?[^:]+):(?\d+):space[/:].+$/i const match = cleanedArn.match(regex) @@ -64,9 +64,16 @@ export function parseArn(arn: string): { region: string; accountId: string } { throw new Error(`Invalid SageMaker ARN format: "${arn}"`) } + // Extract space name from the end of the ARN (after the last forward slash) + const spaceName = cleanedArn.split('/').pop() + if (!spaceName) { + throw new Error(`Could not extract space name from ARN: "${arn}"`) + } + return { region: match.groups.region, accountId: match.groups.account_id, + spaceName: spaceName, } } diff --git a/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts b/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts index 193a11cf972..dd445f344fb 100644 --- a/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts +++ b/packages/core/src/awsService/sagemaker/explorer/sagemakerParentNode.ts @@ -23,6 +23,7 @@ import { getRemoteAppMetadata } from '../remoteUtils' export const parentContextValue = 'awsSagemakerParentNode' export type SelectedDomainUsers = [string, string[]][] +export type SelectedDomainUsersByRegion = [string, SelectedDomainUsers][] export interface UserProfileMetadata { domain: DescribeDomainResponse @@ -133,12 +134,12 @@ export class SagemakerParentNode extends AWSTreeNodeBase { } public async getSelectedDomainUsers(): Promise> { - const selectedDomainUsersMap = new Map( - globals.globalState.get(SagemakerConstants.SelectedDomainUsersState, []) + const selectedDomainUsersByRegionMap = new Map( + globals.globalState.get(SagemakerConstants.SelectedDomainUsersState, []) ) + const selectedDomainUsersMap = new Map(selectedDomainUsersByRegionMap.get(this.regionCode)) const defaultSelectedDomainUsers = await this.getDefaultSelectedDomainUsers() - const cachedDomainUsers = selectedDomainUsersMap.get(this.callerIdentity.Arn || '') if (cachedDomainUsers && cachedDomainUsers.length > 0) { @@ -149,13 +150,19 @@ export class SagemakerParentNode extends AWSTreeNodeBase { } public saveSelectedDomainUsers(selectedDomainUsers: string[]) { - const selectedDomainUsersMap = new Map( - globals.globalState.get(SagemakerConstants.SelectedDomainUsersState, []) + const selectedDomainUsersByRegionMap = new Map( + globals.globalState.get(SagemakerConstants.SelectedDomainUsersState, []) ) + const selectedDomainUsersMap = new Map(selectedDomainUsersByRegionMap.get(this.regionCode)) + if (this.callerIdentity.Arn) { selectedDomainUsersMap?.set(this.callerIdentity.Arn, selectedDomainUsers) - globals.globalState.tryUpdate(SagemakerConstants.SelectedDomainUsersState, [...selectedDomainUsersMap]) + selectedDomainUsersByRegionMap?.set(this.regionCode, [...selectedDomainUsersMap]) + + globals.globalState.tryUpdate(SagemakerConstants.SelectedDomainUsersState, [ + ...selectedDomainUsersByRegionMap, + ]) } } diff --git a/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts b/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts index 16fd00d95cb..6151224a510 100644 --- a/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts +++ b/packages/core/src/awsService/sagemaker/explorer/sagemakerSpaceNode.ts @@ -162,15 +162,17 @@ export class SagemakerSpaceNode extends AWSTreeNodeBase implements AWSResourceNo public async refreshNode(): Promise { await this.updateSpaceAppStatus() - await tryRefreshNode(this) + await vscode.commands.executeCommand('aws.refreshAwsExplorerNode', this) } } export async function tryRefreshNode(node?: SagemakerSpaceNode) { if (node) { - const n = node instanceof SagemakerSpaceNode ? node.parent : node try { - await n.refreshNode() + // For SageMaker spaces, refresh just the individual space node to avoid expensive + // operation of refreshing all spaces in the domain + await node.updateSpaceAppStatus() + await vscode.commands.executeCommand('aws.refreshAwsExplorerNode', node) } catch (e) { getLogger().error('refreshNode failed: %s', (e as Error).message) } diff --git a/packages/core/src/awsService/sagemaker/model.ts b/packages/core/src/awsService/sagemaker/model.ts index 9acf481f2f0..20a667a0bfa 100644 --- a/packages/core/src/awsService/sagemaker/model.ts +++ b/packages/core/src/awsService/sagemaker/model.ts @@ -121,7 +121,7 @@ export async function startLocalServer(ctx: vscode.ExtensionContext) { const errLog = path.join(storagePath, 'sagemaker-local-server.err.log') const infoFilePath = path.join(storagePath, 'sagemaker-local-server-info.json') - logger.info(`local server logs at ${storagePath}/sagemaker-local-server.*.log`) + logger.info(`sagemaker-local-server.*.log at ${storagePath}`) const customEndpoint = DevSettings.instance.get('endpoints', {})['sagemaker'] diff --git a/packages/core/src/awsService/sagemaker/remoteUtils.ts b/packages/core/src/awsService/sagemaker/remoteUtils.ts index ffd7210eea1..9ff8d8ca177 100644 --- a/packages/core/src/awsService/sagemaker/remoteUtils.ts +++ b/packages/core/src/awsService/sagemaker/remoteUtils.ts @@ -5,8 +5,9 @@ import { fs } from '../../shared/fs/fs' import { SagemakerClient } from '../../shared/clients/sagemaker' -import { parseRegionFromArn, RemoteAppMetadata } from './utils' +import { RemoteAppMetadata } from './utils' import { getLogger } from '../../shared/logger/logger' +import { parseArn } from './detached-server/utils' export async function getRemoteAppMetadata(): Promise { try { @@ -21,7 +22,7 @@ export async function getRemoteAppMetadata(): Promise { throw new Error('DomainId or SpaceName not found in metadata file') } - const region = parseRegionFromArn(metadata.ResourceArn) + const { region } = parseArn(metadata.ResourceArn) const client = new SagemakerClient(region) const spaceDetails = await client.describeSpace({ DomainId: domainId, SpaceName: spaceName }) diff --git a/packages/core/src/awsService/sagemaker/uriHandlers.ts b/packages/core/src/awsService/sagemaker/uriHandlers.ts index 8ee91c03d88..17c3c512272 100644 --- a/packages/core/src/awsService/sagemaker/uriHandlers.ts +++ b/packages/core/src/awsService/sagemaker/uriHandlers.ts @@ -7,17 +7,20 @@ import * as vscode from 'vscode' import { SearchParams } from '../../shared/vscode/uriHandler' import { deeplinkConnect } from './commands' import { ExtContext } from '../../shared/extensions' +import { telemetry } from '../../shared/telemetry/telemetry' export function register(ctx: ExtContext) { async function connectHandler(params: ReturnType) { - await deeplinkConnect( - ctx, - params.connection_identifier, - params.session, - `${params.ws_url}&cell-number=${params['cell-number']}`, - params.token, - params.domain - ) + await telemetry.sagemaker_deeplinkConnect.run(async () => { + await deeplinkConnect( + ctx, + params.connection_identifier, + params.session, + `${params.ws_url}&cell-number=${params['cell-number']}`, + params.token, + params.domain + ) + }) } return vscode.Disposable.from(ctx.uriHandler.onPath('/connect/sagemaker', connectHandler, parseConnectParams)) diff --git a/packages/core/src/awsService/sagemaker/utils.ts b/packages/core/src/awsService/sagemaker/utils.ts index 602cb17f6ed..33cc5880bee 100644 --- a/packages/core/src/awsService/sagemaker/utils.ts +++ b/packages/core/src/awsService/sagemaker/utils.ts @@ -4,9 +4,12 @@ */ import * as cp from 'child_process' // eslint-disable-line no-restricted-imports +import * as path from 'path' import { AppStatus, SpaceStatus } from '@aws-sdk/client-sagemaker' import { SagemakerSpaceApp } from '../../shared/clients/sagemaker' import { sshLogFileLocation } from '../../shared/sshConfig' +import { fs } from '../../shared/fs/fs' +import { getLogger } from '../../shared/logger/logger' export const DomainKeyDelimiter = '__' @@ -94,11 +97,60 @@ export function spawnDetachedServer(...args: Parameters) { return cp.spawn(...args) } -export function parseRegionFromArn(arn: string): string { - const parts = arn.split(':') - if (parts.length < 4) { - throw new Error(`Invalid ARN: "${arn}"`) +export const ActivityCheckInterval = 60000 + +/** + * Updates the idle file with the current timestamp + */ +export async function updateIdleFile(idleFilePath: string): Promise { + try { + const timestamp = new Date().toISOString() + await fs.writeFile(idleFilePath, timestamp) + } catch (error) { + getLogger().error(`Failed to update SMAI idle file: ${error}`) + } +} + +/** + * Checks for terminal activity by reading the /dev/pts directory and comparing modification times of the files. + * + * The /dev/pts directory is used in Unix-like operating systems to represent pseudo-terminal (PTY) devices. + * Each active terminal session is assigned a PTY device. These devices are represented as files within the /dev/pts directory. + * When a terminal session has activity, such as when a user inputs commands or output is written to the terminal, + * the modification time (mtime) of the corresponding PTY device file is updated. By monitoring the modification + * times of the files in the /dev/pts directory, we can detect terminal activity. + * + * If activity is detected (i.e., if any PTY device file was modified within the CHECK_INTERVAL), this function + * updates the last activity timestamp. + */ +export async function checkTerminalActivity(idleFilePath: string): Promise { + try { + const files = await fs.readdir('/dev/pts') + const now = Date.now() + + for (const [fileName] of files) { + const filePath = path.join('/dev/pts', fileName) + try { + const stats = await fs.stat(filePath) + const mtime = new Date(stats.mtime).getTime() + if (now - mtime < ActivityCheckInterval) { + await updateIdleFile(idleFilePath) + return + } + } catch (err) { + getLogger().error(`Error reading file stats:`, err) + } + } + } catch (err) { + getLogger().error(`Error reading /dev/pts directory:`, err) } +} - return parts[3] // region is the 4th part +/** + * Starts monitoring terminal activity by setting an interval to check for activity in the /dev/pts directory. + */ +export function startMonitoringTerminalActivity(idleFilePath: string): NodeJS.Timeout { + return setInterval(async () => { + await checkTerminalActivity(idleFilePath) + }, ActivityCheckInterval) } diff --git a/packages/core/src/shared/clients/sagemaker.ts b/packages/core/src/shared/clients/sagemaker.ts index d24a0f74869..8a8e138dd85 100644 --- a/packages/core/src/shared/clients/sagemaker.ts +++ b/packages/core/src/shared/clients/sagemaker.ts @@ -37,9 +37,17 @@ import { isEmpty } from 'lodash' import { sleep } from '../utilities/timeoutUtils' import { ClientWrapper } from './clientWrapper' import { AsyncCollection } from '../utilities/asyncCollection' +import { + InstanceTypeError, + InstanceTypeMinimum, + InstanceTypeInsufficientMemory, + InstanceTypeInsufficientMemoryMessage, + InstanceTypeNotSelectedMessage, +} from '../../awsService/sagemaker/constants' import { getDomainSpaceKey } from '../../awsService/sagemaker/utils' import { getLogger } from '../logger/logger' import { ToolkitError } from '../errors' +import { yes, no, continueText, cancel } from '../localizedText' export interface SagemakerSpaceApp extends SpaceDetails { App?: AppDetails @@ -85,7 +93,9 @@ export class SagemakerClient extends ClientWrapper { } public async startSpace(spaceName: string, domainId: string) { - let spaceDetails + let spaceDetails: DescribeSpaceCommandOutput + + // Get existing space details try { spaceDetails = await this.describeSpace({ DomainId: domainId, @@ -95,6 +105,54 @@ export class SagemakerClient extends ClientWrapper { throw this.handleStartSpaceError(err) } + // Get app type + const appType = spaceDetails.SpaceSettings?.AppType + if (appType !== 'JupyterLab' && appType !== 'CodeEditor') { + throw new ToolkitError(`Unsupported AppType "${appType}" for space "${spaceName}"`) + } + + // Get app resource spec + const requestedResourceSpec = + appType === 'JupyterLab' + ? spaceDetails.SpaceSettings?.JupyterLabAppSettings?.DefaultResourceSpec + : spaceDetails.SpaceSettings?.CodeEditorAppSettings?.DefaultResourceSpec + + let instanceType = requestedResourceSpec?.InstanceType + + // Is InstanceType defined and has enough memory? + if (instanceType && instanceType in InstanceTypeInsufficientMemory) { + // Prompt user to select one with sufficient memory (1 level up from their chosen one) + const response = await vscode.window.showErrorMessage( + InstanceTypeInsufficientMemoryMessage( + spaceDetails.SpaceName || '', + instanceType, + InstanceTypeInsufficientMemory[instanceType] + ), + yes, + no + ) + + if (response === no) { + throw new ToolkitError('InstanceType has insufficient memory.', { code: InstanceTypeError }) + } + + instanceType = InstanceTypeInsufficientMemory[instanceType] + } else if (!instanceType) { + // Prompt user to select the minimum supported instance type + const response = await vscode.window.showErrorMessage( + InstanceTypeNotSelectedMessage(spaceDetails.SpaceName || ''), + continueText, + cancel + ) + + if (response === cancel) { + throw new ToolkitError('InstanceType not defined.', { code: InstanceTypeError }) + } + + instanceType = InstanceTypeMinimum + } + + // Get remote access flag if (!spaceDetails.SpaceSettings?.RemoteAccess || spaceDetails.SpaceSettings?.RemoteAccess === 'DISABLED') { try { await this.updateSpace({ @@ -110,23 +168,17 @@ export class SagemakerClient extends ClientWrapper { } } - const appType = spaceDetails.SpaceSettings?.AppType - if (appType !== 'JupyterLab' && appType !== 'CodeEditor') { - throw new ToolkitError(`Unsupported AppType "${appType}" for space "${spaceName}"`) - } - - const requestedResourceSpec = - appType === 'JupyterLab' - ? spaceDetails.SpaceSettings?.JupyterLabAppSettings?.DefaultResourceSpec - : spaceDetails.SpaceSettings?.CodeEditorAppSettings?.DefaultResourceSpec - - const fallbackResourceSpec: ResourceSpec = { - InstanceType: 'ml.t3.medium', + const resourceSpec: ResourceSpec = { + // Default values SageMakerImageArn: 'arn:aws:sagemaker:us-west-2:542918446943:image/sagemaker-distribution-cpu', SageMakerImageVersionAlias: '3.2.0', - } - const resourceSpec = requestedResourceSpec?.InstanceType ? requestedResourceSpec : fallbackResourceSpec + // The existing resource spec + ...requestedResourceSpec, + + // The instance type user has chosen + InstanceType: instanceType, + } const cleanedResourceSpec = resourceSpec && 'EnvironmentArn' in resourceSpec diff --git a/packages/core/src/shared/remoteSession.ts b/packages/core/src/shared/remoteSession.ts index 282b629df51..b45bdb3ca9c 100644 --- a/packages/core/src/shared/remoteSession.ts +++ b/packages/core/src/shared/remoteSession.ts @@ -25,6 +25,11 @@ import { EvaluationResult } from '@aws-sdk/client-iam' const policyAttachDelay = 5000 +export enum RemoteSessionError { + ExtensionVersionTooLow = 'ExtensionVersionTooLow', + MissingExtension = 'MissingExtension', +} + export interface MissingTool { readonly name: 'code' | 'ssm' | 'ssh' readonly reason?: string @@ -114,13 +119,13 @@ export async function ensureRemoteSshInstalled(): Promise { if (isExtensionInstalled(VSCODE_EXTENSION_ID.remotessh)) { throw new ToolkitError('Remote SSH extension version is too low', { cancelled: true, - code: 'ExtensionVersionTooLow', + code: RemoteSessionError.ExtensionVersionTooLow, details: { expected: vscodeExtensionMinVersion.remotessh }, }) } else { throw new ToolkitError('Remote SSH extension not installed', { cancelled: true, - code: 'MissingExtension', + code: RemoteSessionError.MissingExtension, }) } } diff --git a/packages/core/src/shared/sshConfig.ts b/packages/core/src/shared/sshConfig.ts index db20c173393..bba23b9a4d8 100644 --- a/packages/core/src/shared/sshConfig.ts +++ b/packages/core/src/shared/sshConfig.ts @@ -40,6 +40,9 @@ export class SshConfig { } protected async getProxyCommand(command: string): Promise> { + // Use %n for SageMaker to preserve original hostname case (avoids SSH canonicalization lowercasing and DNS lookup) + const hostnameToken = this.scriptPrefix === 'sagemaker_connect' ? '%n' : '%h' + if (this.isWin()) { // Some older versions of OpenSSH (7.8 and below) have a bug where attempting to use powershell.exe directly will fail without an absolute path const proc = new ChildProcess('powershell.exe', ['-Command', '(get-command powershell.exe).Path']) @@ -47,9 +50,9 @@ export class SshConfig { if (r.exitCode !== 0) { return Result.err(new ToolkitError('Failed to get absolute path for powershell', { cause: r.error })) } - return Result.ok(`"${r.stdout}" -ExecutionPolicy RemoteSigned -File "${command}" %h`) + return Result.ok(`"${r.stdout}" -ExecutionPolicy RemoteSigned -File "${command}" ${hostnameToken}`) } else { - return Result.ok(`'${command}' '%h'`) + return Result.ok(`'${command}' '${hostnameToken}'`) } } diff --git a/packages/core/src/shared/telemetry/vscodeTelemetry.json b/packages/core/src/shared/telemetry/vscodeTelemetry.json index 55f4f8934cf..3bc103d81e3 100644 --- a/packages/core/src/shared/telemetry/vscodeTelemetry.json +++ b/packages/core/src/shared/telemetry/vscodeTelemetry.json @@ -268,6 +268,15 @@ } ] }, + { + "name": "sagemaker_deeplinkConnect", + "description": "Connect to SageMake Space via a deeplink", + "metadata": [ + { + "type": "result" + } + ] + }, { "name": "amazonq_didSelectProfile", "description": "Emitted after the user's Q Profile has been set, whether the user was prompted with a dialog, or a profile was automatically assigned after signing in.", diff --git a/packages/core/src/test/awsService/sagemaker/credentialMapping.test.ts b/packages/core/src/test/awsService/sagemaker/credentialMapping.test.ts index 1d17651a042..06f19a5e890 100644 --- a/packages/core/src/test/awsService/sagemaker/credentialMapping.test.ts +++ b/packages/core/src/test/awsService/sagemaker/credentialMapping.test.ts @@ -12,7 +12,7 @@ import globals from '../../../shared/extensionGlobals' describe('credentialMapping', () => { describe('persistLocalCredentials', () => { - const appArn = 'arn:aws:sagemaker:us-west-2:123456789012:app/domain/space' + const appArn = 'arn:aws:sagemaker:us-west-2:123456789012:space/d-f0lwireyzpjp/test-space' let sandbox: sinon.SinonSandbox @@ -78,8 +78,8 @@ describe('credentialMapping', () => { }) describe('persistSSMConnection', () => { - const appArn = 'arn:aws:sagemaker:us-west-2:123456789012:app/domain/space' - const domain = 'my-domain' + const appArn = 'arn:aws:sagemaker:us-west-2:123456789012:space/d-f0lwireyzpjp/test-space' + const domain = 'd-f0lwireyzpjp' let sandbox: sinon.SinonSandbox beforeEach(() => { @@ -102,6 +102,16 @@ describe('credentialMapping', () => { sandbox.stub(fs, 'existsFile').resolves(false) const writeStub = sandbox.stub(fs, 'writeFile').resolves() + // Stub the AWS API call + const mockDescribeSpace = sandbox.stub().resolves({ + SpaceSettings: { + AppType: 'JupyterLab', + }, + }) + sandbox.stub(require('../../../shared/clients/sagemaker'), 'SagemakerClient').returns({ + describeSpace: mockDescribeSpace, + }) + await persistSSMConnection(appArn, domain) const raw = writeStub.firstCall.args[1] @@ -121,6 +131,16 @@ describe('credentialMapping', () => { sandbox.stub(fs, 'existsFile').resolves(false) const writeStub = sandbox.stub(fs, 'writeFile').resolves() + // Stub the AWS API call + const mockDescribeSpace = sandbox.stub().resolves({ + SpaceSettings: { + AppType: 'JupyterLab', + }, + }) + sandbox.stub(require('../../../shared/clients/sagemaker'), 'SagemakerClient').returns({ + describeSpace: mockDescribeSpace, + }) + await persistSSMConnection(appArn, domain, 'sess', 'wss://ws', 'token') const raw = writeStub.firstCall.args[1] @@ -140,6 +160,16 @@ describe('credentialMapping', () => { sandbox.stub(fs, 'existsFile').resolves(false) const writeStub = sandbox.stub(fs, 'writeFile').resolves() + // Stub the AWS API call + const mockDescribeSpace = sandbox.stub().resolves({ + SpaceSettings: { + AppType: 'JupyterLab', + }, + }) + sandbox.stub(require('../../../shared/clients/sagemaker'), 'SagemakerClient').returns({ + describeSpace: mockDescribeSpace, + }) + await persistSSMConnection(appArn, domain) const raw = writeStub.firstCall.args[1] @@ -150,5 +180,31 @@ describe('credentialMapping', () => { 'loadtest.studio.us-west-2.asfiovnxocqpcry.com' ) }) + + // TODO: Skipped due to hardcoded appSubDomain. Currently hardcoded to 'jupyterlab' due to + // a bug in Studio that only supports refreshing the token for both CodeEditor and JupyterLab + // Apps in the jupyterlab subdomain. This will be fixed shortly after NYSummit launch to + // support refresh URL in CodeEditor subdomain. Additionally, appType will be determined by + // the deeplink URI rather than the describeSpace call from the toolkit. + it.skip('throws error when app type is unsupported', async () => { + sandbox.stub(DevSettings.instance, 'get').returns({}) + sandbox.stub(fs, 'existsFile').resolves(false) + + // Stub the AWS API call to return an unsupported app type + const mockDescribeSpace = sandbox.stub().resolves({ + SpaceSettings: { + AppType: 'UnsupportedApp', + }, + }) + sandbox.stub(require('../../../shared/clients/sagemaker'), 'SagemakerClient').returns({ + describeSpace: mockDescribeSpace, + }) + + await assert.rejects(() => persistSSMConnection(appArn, domain), { + name: 'Error', + message: + 'Unsupported or missing app type for space. Expected JupyterLab or CodeEditor, got: UnsupportedApp', + }) + }) }) }) diff --git a/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSession.test.ts b/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSession.test.ts index 1e09fdbc8da..5b3f176a29f 100644 --- a/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSession.test.ts +++ b/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSession.test.ts @@ -42,6 +42,7 @@ describe('handleGetSession', () => { sinon.stub(utils, 'parseArn').returns({ region: 'us-west-2', accountId: '123456789012', + spaceName: 'space-name', }) await handleGetSession(req as http.IncomingMessage, res as http.ServerResponse) @@ -56,6 +57,7 @@ describe('handleGetSession', () => { sinon.stub(utils, 'parseArn').returns({ region: 'us-west-2', accountId: '123456789012', + spaceName: 'space-name', }) sinon.stub(utils, 'startSagemakerSession').rejects(new Error('session error')) @@ -71,6 +73,7 @@ describe('handleGetSession', () => { sinon.stub(utils, 'parseArn').returns({ region: 'us-west-2', accountId: '123456789012', + spaceName: 'space-name', }) sinon.stub(utils, 'startSagemakerSession').resolves({ SessionId: 'abc123', diff --git a/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSessionAsync.test.ts b/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSessionAsync.test.ts index f8d76912b2b..8d3ab8563ee 100644 --- a/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSessionAsync.test.ts +++ b/packages/core/src/test/awsService/sagemaker/detached-server/routes/getSessionAsync.test.ts @@ -8,6 +8,7 @@ import * as sinon from 'sinon' import assert from 'assert' import { SessionStore } from '../../../../../awsService/sagemaker/detached-server/sessionStore' import { handleGetSessionAsync } from '../../../../../awsService/sagemaker/detached-server/routes/getSessionAsync' +import * as utils from '../../../../../awsService/sagemaker/detached-server/utils' describe('handleGetSessionAsync', () => { let req: Partial @@ -51,46 +52,46 @@ describe('handleGetSessionAsync', () => { }) }) - // Temporarily disabling reconnect logic for the 7/3 Phase 1 launch. - // Will re-enable in the next release around 7/14. - - // it('responds with 204 if session is pending', async () => { - // req = { url: '/session_async?connection_identifier=abc&request_id=req123' } - // storeStub.getFreshEntry.returns(Promise.resolve(undefined)) - // storeStub.getStatus.returns(Promise.resolve('pending')) + it('responds with 204 if session is pending', async () => { + req = { url: '/session_async?connection_identifier=abc&request_id=req123' } + storeStub.getFreshEntry.returns(Promise.resolve(undefined)) + storeStub.getStatus.returns(Promise.resolve('pending')) - // await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) + await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) - // assert(resWriteHead.calledWith(204)) - // assert(resEnd.calledOnce) - // }) + assert(resWriteHead.calledWith(204)) + assert(resEnd.calledOnce) + }) - // it('responds with 202 if status is not-started and opens browser', async () => { - // req = { url: '/session_async?connection_identifier=abc&request_id=req123' } + it('responds with 202 if status is not-started and opens browser', async () => { + req = { url: '/session_async?connection_identifier=abc&request_id=req123' } - // storeStub.getFreshEntry.returns(Promise.resolve(undefined)) - // storeStub.getStatus.returns(Promise.resolve('not-started')) - // storeStub.getRefreshUrl.returns(Promise.resolve('https://example.com/refresh')) - // storeStub.markPending.returns(Promise.resolve()) + storeStub.getFreshEntry.returns(Promise.resolve(undefined)) + storeStub.getStatus.returns(Promise.resolve('not-started')) + storeStub.getRefreshUrl.returns(Promise.resolve('https://example.com/refresh')) + storeStub.markPending.returns(Promise.resolve()) - // sinon.stub(utils, 'readServerInfo').resolves({ pid: 1234, port: 4567 }) - // sinon.stub(utils, 'open').resolves() - // await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) + sinon.stub(utils, 'readServerInfo').resolves({ pid: 1234, port: 4567 }) + sinon + .stub(utils, 'parseArn') + .returns({ region: 'us-east-1', accountId: '123456789012', spaceName: 'test-space' }) + sinon.stub(utils, 'open').resolves() + await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) - // assert(resWriteHead.calledWith(202)) - // assert(resEnd.calledWithMatch(/Session is not ready yet/)) - // assert(storeStub.markPending.calledWith('abc', 'req123')) - // }) + assert(resWriteHead.calledWith(202)) + assert(resEnd.calledWithMatch(/Session is not ready yet/)) + assert(storeStub.markPending.calledWith('abc', 'req123')) + }) - // it('responds with 500 if unexpected error occurs', async () => { - // req = { url: '/session_async?connection_identifier=abc&request_id=req123' } - // storeStub.getFreshEntry.throws(new Error('fail')) + it('responds with 500 if unexpected error occurs', async () => { + req = { url: '/session_async?connection_identifier=abc&request_id=req123' } + storeStub.getFreshEntry.throws(new Error('fail')) - // await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) + await handleGetSessionAsync(req as http.IncomingMessage, res as http.ServerResponse) - // assert(resWriteHead.calledWith(500)) - // assert(resEnd.calledWith('Unexpected error')) - // }) + assert(resWriteHead.calledWith(500)) + assert(resEnd.calledWith('Unexpected error')) + }) afterEach(() => { sinon.restore() diff --git a/packages/core/src/test/awsService/sagemaker/detached-server/sessionStore.test.ts b/packages/core/src/test/awsService/sagemaker/detached-server/sessionStore.test.ts index 0bb46b7d24b..2a7828a4951 100644 --- a/packages/core/src/test/awsService/sagemaker/detached-server/sessionStore.test.ts +++ b/packages/core/src/test/awsService/sagemaker/detached-server/sessionStore.test.ts @@ -59,7 +59,7 @@ describe('SessionStore', () => { assert(writeMappingStub.calledOnce) }) - it('returns async fresh entry and marks consumed', async () => { + it('returns async fresh entry and deletes it', async () => { const store = new SessionStore() // Disable initial-connection freshness readMappingStub.returns({ @@ -77,6 +77,10 @@ describe('SessionStore', () => { assert.ok(result, 'Expected result to be defined') assert.strictEqual(result.sessionId, 'a') assert(writeMappingStub.calledOnce) + + // Verify the entry was deleted from the mapping + const updated = writeMappingStub.firstCall.args[0] + assert.strictEqual(updated.deepLink[connectionId].requests[requestId], undefined) }) it('returns undefined if no fresh entries exist', async () => { diff --git a/packages/core/src/test/awsService/sagemaker/detached-server/utils.test.ts b/packages/core/src/test/awsService/sagemaker/detached-server/utils.test.ts index 66a47747bf9..1eeb5708d11 100644 --- a/packages/core/src/test/awsService/sagemaker/detached-server/utils.test.ts +++ b/packages/core/src/test/awsService/sagemaker/detached-server/utils.test.ts @@ -8,29 +8,22 @@ import { parseArn } from '../../../../awsService/sagemaker/detached-server/utils describe('parseArn', () => { it('parses a standard SageMaker ARN with forward slash', () => { - const arn = 'arn:aws:sagemaker:us-west-2:123456789012:space/my-space-name' + const arn = 'arn:aws:sagemaker:us-west-2:123456789012:space/domain-name/my-space-name' const result = parseArn(arn) assert.deepStrictEqual(result, { region: 'us-west-2', accountId: '123456789012', - }) - }) - - it('parses a standard SageMaker ARN with colon', () => { - const arn = 'arn:aws:sagemaker:eu-central-1:123456789012:space:space-name' - const result = parseArn(arn) - assert.deepStrictEqual(result, { - region: 'eu-central-1', - accountId: '123456789012', + spaceName: 'my-space-name', }) }) it('parses an ARN prefixed with sagemaker-user@', () => { - const arn = 'sagemaker-user@arn:aws:sagemaker:ap-southeast-1:123456789012:space/foo' + const arn = 'sagemaker-user@arn:aws:sagemaker:ap-southeast-1:123456789012:space/foo/my-space-name' const result = parseArn(arn) assert.deepStrictEqual(result, { region: 'ap-southeast-1', accountId: '123456789012', + spaceName: 'my-space-name', }) }) diff --git a/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts b/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts index a0a0f807b73..8fccfe4bfd9 100644 --- a/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts +++ b/packages/core/src/test/awsService/sagemaker/explorer/sagemakerParentNode.test.ts @@ -8,7 +8,13 @@ import * as vscode from 'vscode' import { DescribeDomainResponse } from '@amzn/sagemaker-client' import { GetCallerIdentityResponse } from 'aws-sdk/clients/sts' import { SagemakerClient, SagemakerSpaceApp } from '../../../../shared/clients/sagemaker' -import { SagemakerParentNode } from '../../../../awsService/sagemaker/explorer/sagemakerParentNode' +import { SagemakerConstants } from '../../../../awsService/sagemaker/explorer/constants' +import { + SagemakerParentNode, + SelectedDomainUsers, + SelectedDomainUsersByRegion, +} from '../../../../awsService/sagemaker/explorer/sagemakerParentNode' +import { globals } from '../../../../shared' import { DefaultStsClient } from '../../../../shared/clients/stsClient' import { assertNodeListOnlyHasPlaceholderNode } from '../../../utilities/explorerNodeAssertions' import assert from 'assert' @@ -26,6 +32,71 @@ describe('sagemakerParentNode', function () { ['domain1', { DomainId: 'domain1', DomainName: 'domainName1' }], ['domain2', { DomainId: 'domain2', DomainName: 'domainName2' }], ]) + const spaceAppsMap: Map = new Map([ + [ + 'domain1__name1', + { + SpaceName: 'name1', + DomainId: 'domain1', + OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, + Status: 'InService', + DomainSpaceKey: 'domain1__name1', + }, + ], + [ + 'domain2__name2', + { + SpaceName: 'name2', + DomainId: 'domain2', + OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, + Status: 'InService', + DomainSpaceKey: 'domain2__name2', + }, + ], + ]) + const spaceAppsMapPending: Map = new Map([ + [ + 'domain1__name3', + { + SpaceName: 'name3', + DomainId: 'domain1', + OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, + Status: 'InService', + DomainSpaceKey: 'domain1__name3', + App: { + Status: 'InService', + }, + }, + ], + [ + 'domain2__name4', + { + SpaceName: 'name4', + DomainId: 'domain2', + OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, + Status: 'InService', + DomainSpaceKey: 'domain2__name4', + App: { + Status: 'Pending', + }, + }, + ], + ]) + const iamUser = { + UserId: 'test-userId', + Account: '123456789012', + Arn: 'arn:aws:iam::123456789012:user/user2', + } + const assumedRoleUser = { + UserId: 'test-userId', + Account: '123456789012', + Arn: 'arn:aws:sts::123456789012:assumed-role/UserRole/user2', + } + const ssoUser = { + UserId: 'test-userId', + Account: '123456789012', + Arn: 'arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_MyPermissionSet_abcd1234/user2', + } const getConfigTrue = { get: () => true, } @@ -55,50 +126,15 @@ describe('sagemakerParentNode', function () { fetchSpaceAppsAndDomainsStub.returns( Promise.resolve([new Map(), new Map()]) ) - getCallerIdentityStub.returns( - Promise.resolve({ - UserId: 'test-userId', - Account: '123456789012', - Arn: 'arn:aws:iam::123456789012:user/test-user', - }) - ) + getCallerIdentityStub.returns(Promise.resolve(iamUser)) const childNodes = await testNode.getChildren() assertNodeListOnlyHasPlaceholderNode(childNodes) }) it('has child nodes', async function () { - const spaceAppsMap: Map = new Map([ - [ - 'domain1__name1', - { - SpaceName: 'name1', - DomainId: 'domain1', - OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, - Status: 'InService', - DomainSpaceKey: 'domain1__name1', - }, - ], - [ - 'domain2__name2', - { - SpaceName: 'name2', - DomainId: 'domain2', - OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, - Status: 'InService', - DomainSpaceKey: 'domain2__name2', - }, - ], - ]) - fetchSpaceAppsAndDomainsStub.returns(Promise.resolve([spaceAppsMap, domainsMap])) - getCallerIdentityStub.returns( - Promise.resolve({ - UserId: 'test-userId', - Account: '123456789012', - Arn: 'arn:aws:iam::123456789012:user/test-user', - }) - ) + getCallerIdentityStub.returns(Promise.resolve(iamUser)) sinon .stub(vscode.workspace, 'getConfiguration') .returns(getConfigFalse as unknown as vscode.WorkspaceConfiguration) @@ -110,43 +146,8 @@ describe('sagemakerParentNode', function () { }) it('adds pending nodes to polling nodes set', async function () { - const spaceAppsMap: Map = new Map([ - [ - 'domain1__name3', - { - SpaceName: 'name3', - DomainId: 'domain1', - OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, - Status: 'InService', - DomainSpaceKey: 'domain1__name3', - App: { - Status: 'InService', - }, - }, - ], - [ - 'domain2__name4', - { - SpaceName: 'name4', - DomainId: 'domain2', - OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, - Status: 'InService', - DomainSpaceKey: 'domain2__name4', - App: { - Status: 'Pending', - }, - }, - ], - ]) - - fetchSpaceAppsAndDomainsStub.returns(Promise.resolve([spaceAppsMap, domainsMap])) - getCallerIdentityStub.returns( - Promise.resolve({ - UserId: 'test-userId', - Account: '123456789012', - Arn: 'arn:aws:iam::123456789012:user/test-user', - }) - ) + fetchSpaceAppsAndDomainsStub.returns(Promise.resolve([spaceAppsMapPending, domainsMap])) + getCallerIdentityStub.returns(Promise.resolve(iamUser)) await testNode.updateChildren() assert.strictEqual(testNode.pollingSet.size, 1) @@ -154,37 +155,8 @@ describe('sagemakerParentNode', function () { }) it('filters spaces owned by user profiles that match the IAM user', async function () { - const spaceAppsMap: Map = new Map([ - [ - 'domain1__name1', - { - SpaceName: 'name1', - DomainId: 'domain1', - OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, - Status: 'InService', - DomainSpaceKey: 'domain1__name1', - }, - ], - [ - 'domain2__name2', - { - SpaceName: 'name2', - DomainId: 'domain2', - OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, - Status: 'InService', - DomainSpaceKey: 'domain2__name2', - }, - ], - ]) - fetchSpaceAppsAndDomainsStub.returns(Promise.resolve([spaceAppsMap, domainsMap])) - getCallerIdentityStub.returns( - Promise.resolve({ - UserId: 'test-userId', - Account: '123456789012', - Arn: 'arn:aws:iam::123456789012:user/user2', - }) - ) + getCallerIdentityStub.returns(Promise.resolve(iamUser)) sinon .stub(vscode.workspace, 'getConfiguration') .returns(getConfigTrue as unknown as vscode.WorkspaceConfiguration) @@ -195,37 +167,8 @@ describe('sagemakerParentNode', function () { }) it('filters spaces owned by user profiles that match the IAM assumed-role session name', async function () { - const spaceAppsMap: Map = new Map([ - [ - 'domain1__name1', - { - SpaceName: 'name1', - DomainId: 'domain1', - OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, - Status: 'InService', - DomainSpaceKey: 'domain1__name1', - }, - ], - [ - 'domain2__name2', - { - SpaceName: 'name2', - DomainId: 'domain2', - OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, - Status: 'InService', - DomainSpaceKey: 'domain2__name2', - }, - ], - ]) - fetchSpaceAppsAndDomainsStub.returns(Promise.resolve([spaceAppsMap, domainsMap])) - getCallerIdentityStub.returns( - Promise.resolve({ - UserId: 'test-userId', - Account: '123456789012', - Arn: 'arn:aws:sts::123456789012:assumed-role/UserRole/user2', - }) - ) + getCallerIdentityStub.returns(Promise.resolve(assumedRoleUser)) sinon .stub(vscode.workspace, 'getConfiguration') .returns(getConfigTrue as unknown as vscode.WorkspaceConfiguration) @@ -236,37 +179,8 @@ describe('sagemakerParentNode', function () { }) it('filters spaces owned by user profiles that match the Identity Center user', async function () { - const spaceAppsMap: Map = new Map([ - [ - 'domain1__name1', - { - SpaceName: 'name1', - DomainId: 'domain1', - OwnershipSettingsSummary: { OwnerUserProfileName: 'user1-abcd' }, - Status: 'InService', - DomainSpaceKey: 'domain1__name1', - }, - ], - [ - 'domain2__name2', - { - SpaceName: 'name2', - DomainId: 'domain2', - OwnershipSettingsSummary: { OwnerUserProfileName: 'user2-efgh' }, - Status: 'InService', - DomainSpaceKey: 'domain2__name2', - }, - ], - ]) - fetchSpaceAppsAndDomainsStub.returns(Promise.resolve([spaceAppsMap, domainsMap])) - getCallerIdentityStub.returns( - Promise.resolve({ - UserId: 'test-userId', - Account: '123456789012', - Arn: 'arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_MyPermissionSet_abcd1234/user2', - }) - ) + getCallerIdentityStub.returns(Promise.resolve(ssoUser)) sinon .stub(vscode.workspace, 'getConfiguration') .returns(getConfigFalse as unknown as vscode.WorkspaceConfiguration) @@ -276,6 +190,81 @@ describe('sagemakerParentNode', function () { assert.strictEqual(childNodes[0].label, 'name2 (Stopped)', 'Unexpected node label') }) + describe('getSelectedDomainUsers', function () { + let originalState: Map + + beforeEach(async function () { + testNode = new SagemakerParentNode(testRegion, client) + originalState = new Map( + globals.globalState.get(SagemakerConstants.SelectedDomainUsersState, []) + ) + }) + + afterEach(async function () { + await globals.globalState.update(SagemakerConstants.SelectedDomainUsersState, [...originalState]) + }) + + it('gets cached selectedDomainUsers for a given region', async function () { + await globals.globalState.update(SagemakerConstants.SelectedDomainUsersState, [ + [testRegion, [['arn:aws:iam::123456789012:user/user2', ['domain2__user-cached']]]], + ]) + testNode.callerIdentity = iamUser + sinon + .stub(vscode.workspace, 'getConfiguration') + .returns(getConfigTrue as unknown as vscode.WorkspaceConfiguration) + + const result = await testNode.getSelectedDomainUsers() + assert.deepStrictEqual( + [...result], + ['domain2__user-cached'], + 'Should match only cached selected domain user' + ) + }) + + it('gets default selectedDomainUsers', async function () { + await globals.globalState.update(SagemakerConstants.SelectedDomainUsersState, []) + testNode.spaceApps = spaceAppsMap + testNode.callerIdentity = iamUser + sinon + .stub(vscode.workspace, 'getConfiguration') + .returns(getConfigTrue as unknown as vscode.WorkspaceConfiguration) + + const result = await testNode.getSelectedDomainUsers() + assert.deepStrictEqual( + [...result], + ['domain2__user2-efgh'], + 'Should match only default selected domain user' + ) + }) + }) + + describe('saveSelectedDomainUsers', function () { + let originalState: Map + + beforeEach(async function () { + testNode = new SagemakerParentNode(testRegion, client) + originalState = new Map( + globals.globalState.get(SagemakerConstants.SelectedDomainUsersState, []) + ) + }) + + afterEach(async function () { + await globals.globalState.update(SagemakerConstants.SelectedDomainUsersState, [...originalState]) + }) + + it('saves selectedDomainUsers for a given region', async function () { + testNode.callerIdentity = iamUser + testNode.saveSelectedDomainUsers(['domain1__user-1', 'domain2__user-2']) + + const selectedDomainUsersByRegionMap = new Map( + globals.globalState.get(SagemakerConstants.SelectedDomainUsersState, []) + ) + const selectedDomainUsers = new Map(selectedDomainUsersByRegionMap.get(testRegion)) + + assert.deepStrictEqual(selectedDomainUsers.get(iamUser.Arn), ['domain1__user-1', 'domain2__user-2']) + }) + }) + describe('getLocalSelectedDomainUsers', function () { const createSpaceApp = (ownerName: string): SagemakerSpaceApp => ({ SpaceName: 'space1', diff --git a/packages/core/src/test/awsService/sagemaker/remoteUtils.test.ts b/packages/core/src/test/awsService/sagemaker/remoteUtils.test.ts index b2e1071e0db..4168dfdeee4 100644 --- a/packages/core/src/test/awsService/sagemaker/remoteUtils.test.ts +++ b/packages/core/src/test/awsService/sagemaker/remoteUtils.test.ts @@ -38,8 +38,10 @@ describe('getRemoteAppMetadata', function () { beforeEach(() => { sandbox = sinon.createSandbox() fsStub = sandbox.stub(fs, 'readFileText') - parseRegionStub = sandbox.stub().returns('us-west-2') - sandbox.replace(require('../../../awsService/sagemaker/utils'), 'parseRegionFromArn', parseRegionStub) + parseRegionStub = sandbox + .stub() + .returns({ region: 'us-west-2', accountId: '123456789012', spaceName: 'test-space' }) + sandbox.replace(require('../../../awsService/sagemaker/detached-server/utils'), 'parseArn', parseRegionStub) describeSpaceStub = sandbox.stub().resolves(mockSpaceDetails) sandbox.stub(SagemakerClient.prototype, 'describeSpace').callsFake(describeSpaceStub) diff --git a/packages/core/src/test/awsService/sagemaker/utils.test.ts b/packages/core/src/test/awsService/sagemaker/utils.test.ts index b7376790106..c73fd6968fc 100644 --- a/packages/core/src/test/awsService/sagemaker/utils.test.ts +++ b/packages/core/src/test/awsService/sagemaker/utils.test.ts @@ -4,8 +4,11 @@ */ import { AppStatus, SpaceStatus } from '@aws-sdk/client-sagemaker' -import { generateSpaceStatus } from '../../../awsService/sagemaker/utils' +import { generateSpaceStatus, ActivityCheckInterval } from '../../../awsService/sagemaker/utils' import * as assert from 'assert' +import * as sinon from 'sinon' +import { fs } from '../../../shared/fs/fs' +import * as utils from '../../../awsService/sagemaker/utils' describe('generateSpaceStatus', function () { it('returns Failed if space status is Failed', function () { @@ -64,3 +67,84 @@ describe('generateSpaceStatus', function () { ) }) }) + +describe('checkTerminalActivity', function () { + let sandbox: sinon.SinonSandbox + let fsReaddirStub: sinon.SinonStub + let fsStatStub: sinon.SinonStub + let fsWriteFileStub: sinon.SinonStub + + beforeEach(function () { + sandbox = sinon.createSandbox() + fsReaddirStub = sandbox.stub(fs, 'readdir') + fsStatStub = sandbox.stub(fs, 'stat') + fsWriteFileStub = sandbox.stub(fs, 'writeFile') + }) + + afterEach(function () { + sandbox.restore() + }) + + it('should write to idle file when recent terminal activity is detected', async function () { + const idleFilePath = '/tmp/test-idle-file' + const recentTime = Date.now() - ActivityCheckInterval / 2 // Recent activity + + fsReaddirStub.resolves([ + ['pts1', 1], + ['pts2', 1], + ]) // Mock file entries + fsStatStub.onFirstCall().resolves({ mtime: new Date(recentTime) }) + fsWriteFileStub.resolves() + + await utils.checkTerminalActivity(idleFilePath) + + // Verify that fs.writeFile was called (which means updateIdleFile was called) + assert.strictEqual(fsWriteFileStub.callCount, 1) + assert.strictEqual(fsWriteFileStub.firstCall.args[0], idleFilePath) + + // Verify the timestamp is a valid ISO string + const timestamp = fsWriteFileStub.firstCall.args[1] + assert.strictEqual(typeof timestamp, 'string') + assert.ok(!isNaN(Date.parse(timestamp))) + }) + + it('should stop checking once activity is detected', async function () { + const idleFilePath = '/tmp/test-idle-file' + const recentTime = Date.now() - ActivityCheckInterval / 2 + + fsReaddirStub.resolves([ + ['pts1', 1], + ['pts2', 1], + ['pts3', 1], + ]) + fsStatStub.onFirstCall().resolves({ mtime: new Date(recentTime) }) // First file has activity + fsWriteFileStub.resolves() + + await utils.checkTerminalActivity(idleFilePath) + + // Should only call stat once since activity was detected on first file + assert.strictEqual(fsStatStub.callCount, 1) + // Should write to file once + assert.strictEqual(fsWriteFileStub.callCount, 1) + }) + + it('should handle stat error gracefully and continue checking other files', async function () { + const idleFilePath = '/tmp/test-idle-file' + const recentTime = Date.now() - ActivityCheckInterval / 2 + const statError = new Error('File not found') + + fsReaddirStub.resolves([ + ['pts1', 1], + ['pts2', 1], + ]) + fsStatStub.onFirstCall().rejects(statError) // First file fails + fsStatStub.onSecondCall().resolves({ mtime: new Date(recentTime) }) // Second file succeeds + fsWriteFileStub.resolves() + + await utils.checkTerminalActivity(idleFilePath) + + // Should continue and find activity on second file + assert.strictEqual(fsStatStub.callCount, 2) + assert.strictEqual(fsWriteFileStub.callCount, 1) + }) +}) diff --git a/packages/core/src/test/shared/clients/sagemakerClient.test.ts b/packages/core/src/test/shared/clients/sagemakerClient.test.ts index 94a07dd32eb..888d2222692 100644 --- a/packages/core/src/test/shared/clients/sagemakerClient.test.ts +++ b/packages/core/src/test/shared/clients/sagemakerClient.test.ts @@ -131,7 +131,7 @@ describe('SagemakerClient.fetchSpaceAppsAndDomains', function () { AppType: 'CodeEditor', CodeEditorAppSettings: { DefaultResourceSpec: { - InstanceType: 'ml.t3.medium', + InstanceType: 'ml.t3.large', SageMakerImageArn: 'arn:aws:sagemaker:us-west-2:img', SageMakerImageVersionAlias: '1.0.0', }, @@ -155,7 +155,13 @@ describe('SagemakerClient.fetchSpaceAppsAndDomains', function () { SpaceSettings: { RemoteAccess: 'ENABLED', AppType: 'CodeEditor', - CodeEditorAppSettings: {}, + CodeEditorAppSettings: { + DefaultResourceSpec: { + InstanceType: 'ml.t3.large', + SageMakerImageArn: 'arn:aws:sagemaker:us-west-2:img', + SageMakerImageVersionAlias: '1.0.0', + }, + }, }, }) @@ -184,7 +190,11 @@ describe('SagemakerClient.fetchSpaceAppsAndDomains', function () { SpaceSettings: { RemoteAccess: 'ENABLED', AppType: 'JupyterLab', - JupyterLabAppSettings: {}, + JupyterLabAppSettings: { + DefaultResourceSpec: { + InstanceType: 'ml.t3.large', + }, + }, }, }) @@ -194,7 +204,11 @@ describe('SagemakerClient.fetchSpaceAppsAndDomains', function () { sinon.assert.calledOnceWithExactly( createAppStub, - sinon.match.hasNested('ResourceSpec.InstanceType', 'ml.t3.medium') + sinon.match.hasNested('ResourceSpec', { + InstanceType: 'ml.t3.large', + SageMakerImageArn: 'arn:aws:sagemaker:us-west-2:542918446943:image/sagemaker-distribution-cpu', + SageMakerImageVersionAlias: '3.2.0', + }) ) }) diff --git a/packages/core/src/test/shared/sshConfig.test.ts b/packages/core/src/test/shared/sshConfig.test.ts index 96ca450ae14..03841644e24 100644 --- a/packages/core/src/test/shared/sshConfig.test.ts +++ b/packages/core/src/test/shared/sshConfig.test.ts @@ -82,6 +82,16 @@ describe('VscodeRemoteSshConfig', async function () { const command = result.unwrap() assert.strictEqual(command, testProxyCommand) }) + + it('uses %n token for sagemaker_connect to preserve hostname case', async function () { + const sagemakerConfig = new MockSshConfig('sshPath', 'testHostNamePrefix', 'sagemaker_connect') + sagemakerConfig.testIsWin = false + + const result = await sagemakerConfig.getProxyCommandWrapper('sagemaker_connect') + assert.ok(result.isOk()) + const command = result.unwrap() + assert.strictEqual(command, `'sagemaker_connect' '%n'`) + }) }) describe('matchSshSection', async function () { diff --git a/packages/toolkit/.changes/next-release/Bug Fix-25f95c85-8b96-4cbd-bc3e-da833340be06.json b/packages/toolkit/.changes/next-release/Bug Fix-25f95c85-8b96-4cbd-bc3e-da833340be06.json new file mode 100644 index 00000000000..aa8be24a11c --- /dev/null +++ b/packages/toolkit/.changes/next-release/Bug Fix-25f95c85-8b96-4cbd-bc3e-da833340be06.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "SageMaker: Enable per-region manual filtering of Spaces" +} diff --git a/packages/toolkit/.changes/next-release/Bug Fix-2e0b8ccd-08eb-46e6-947d-27ef5701aca8.json b/packages/toolkit/.changes/next-release/Bug Fix-2e0b8ccd-08eb-46e6-947d-27ef5701aca8.json new file mode 100644 index 00000000000..14364c43d14 --- /dev/null +++ b/packages/toolkit/.changes/next-release/Bug Fix-2e0b8ccd-08eb-46e6-947d-27ef5701aca8.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "SageMaker: Show error message when connecting remotely from a remote workspace" +} diff --git a/packages/toolkit/.changes/next-release/Bug Fix-3d681d73-4171-44f3-a5ee-50f192a398ca.json b/packages/toolkit/.changes/next-release/Bug Fix-3d681d73-4171-44f3-a5ee-50f192a398ca.json new file mode 100644 index 00000000000..b1c50349e5d --- /dev/null +++ b/packages/toolkit/.changes/next-release/Bug Fix-3d681d73-4171-44f3-a5ee-50f192a398ca.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "SageMaker: Prompt user to use upgraded instance type if the chosen one has insufficient memory" +} diff --git a/packages/toolkit/.changes/next-release/Bug Fix-da7c8255-3962-49eb-b4e6-6091eb788bca.json b/packages/toolkit/.changes/next-release/Bug Fix-da7c8255-3962-49eb-b4e6-6091eb788bca.json new file mode 100644 index 00000000000..3b45f594ad6 --- /dev/null +++ b/packages/toolkit/.changes/next-release/Bug Fix-da7c8255-3962-49eb-b4e6-6091eb788bca.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "SageMaker: Resolve connection issues to SageMaker Spaces with capital letters in the name" +} diff --git a/packages/toolkit/.changes/next-release/Feature-3fa7c727-cb0a-439c-9bfe-34a2a1fa99de.json b/packages/toolkit/.changes/next-release/Feature-3fa7c727-cb0a-439c-9bfe-34a2a1fa99de.json new file mode 100644 index 00000000000..db431135a2c --- /dev/null +++ b/packages/toolkit/.changes/next-release/Feature-3fa7c727-cb0a-439c-9bfe-34a2a1fa99de.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "SageMaker: Add support for deep-linked Space reconnection" +} diff --git a/packages/toolkit/.changes/next-release/Feature-d97769de-7dd4-4fe6-a3ba-c951994e6231.json b/packages/toolkit/.changes/next-release/Feature-d97769de-7dd4-4fe6-a3ba-c951994e6231.json new file mode 100644 index 00000000000..8117953da02 --- /dev/null +++ b/packages/toolkit/.changes/next-release/Feature-d97769de-7dd4-4fe6-a3ba-c951994e6231.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "SageMaker: Enable auto-shutdown support for Spaces" +} From 2eb0891e7904b54d1a23d2fbd6f95780c4333bbe Mon Sep 17 00:00:00 2001 From: aws-asolidu Date: Wed, 16 Jul 2025 12:51:20 -0700 Subject: [PATCH 07/69] fix(sagemaker): Fix race-condition with multiple remote spaces trying to reconnect (#7684) ## Problem - When reconnecting to multiple SageMaker Spaces (either via deeplink or from within the VS Code extension), a **race condition** occurs when writing to shared temporary files. This can cause the local SageMaker server to crash due to concurrent access. - Need clearer error messaging when reconnection to a deeplinked space is attempted without an active Studio login. ## Solution - For connections initiated from the VS Code extension, we generate **unique temporary files** to read the response json. - For deeplink-based reconnections, we introduce a **queue** to process session requests sequentially. - Add `remote_access_token_refresh` flag to the refresh URL to enable the Studio server to return more specific error messages. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/core/resources/sagemaker_connect | 22 ++++-- .../detached-server/routes/getSessionAsync.ts | 2 +- .../sagemaker/detached-server/utils.ts | 52 +++++++++++-- .../sagemaker/detached-server/utils.test.ts | 75 ++++++++++++++++++- ...-c6e841d7-e0bb-474c-9540-f896746d26d4.json | 4 + 5 files changed, 141 insertions(+), 14 deletions(-) create mode 100644 packages/toolkit/.changes/next-release/Bug Fix-c6e841d7-e0bb-474c-9540-f896746d26d4.json diff --git a/packages/core/resources/sagemaker_connect b/packages/core/resources/sagemaker_connect index a1b7c9c0db0..19d0e1984cc 100755 --- a/packages/core/resources/sagemaker_connect +++ b/packages/core/resources/sagemaker_connect @@ -9,13 +9,16 @@ _get_ssm_session_info() { local url_to_get_session_info="http://localhost:${local_endpoint_port}/get_session?connection_identifier=${aws_resource_arn}&credentials_type=${credentials_type}" + # Generate unique temporary file name to avoid conflicts + local temp_file="/tmp/ssm_session_response_$$_$(date +%s%N).json" + # Use curl with --write-out to capture HTTP status - response=$(curl -s -w "%{http_code}" -o /tmp/ssm_session_response.json "$url_to_get_session_info") + response=$(curl -s -w "%{http_code}" -o "$temp_file" "$url_to_get_session_info") http_status="${response: -3}" - session_json=$(cat /tmp/ssm_session_response.json) + session_json=$(cat "$temp_file") # Clean up temporary file - rm -f /tmp/ssm_session_response.json + rm -f "$temp_file" if [[ "$http_status" -ne 200 ]]; then echo "Error: Failed to get SSM session info. HTTP status: $http_status" @@ -40,16 +43,21 @@ _get_ssm_session_info_async() { local url_base="http://localhost:${local_endpoint_port}/get_session_async" local url_to_get_session_info="${url_base}?connection_identifier=${aws_resource_arn}&credentials_type=${credentials_type}&request_id=${request_id}" + # Generate unique temporary file name to avoid conflicts + local temp_file="/tmp/ssm_session_response_$$_$(date +%s%N).json" + local max_retries=60 local retry_interval=5 local attempt=1 while (( attempt <= max_retries )); do - response=$(curl -s -w "%{http_code}" -o /tmp/ssm_session_response.json "$url_to_get_session_info") + response=$(curl -s -w "%{http_code}" -o "$temp_file" "$url_to_get_session_info") http_status="${response: -3}" - session_json=$(cat /tmp/ssm_session_response.json) + session_json=$(cat "$temp_file") if [[ "$http_status" -eq 200 ]]; then + # Clean up temporary file on success + rm -f "$temp_file" export SSM_SESSION_JSON="$session_json" return 0 elif [[ "$http_status" -eq 202 || "$http_status" -eq 204 ]]; then @@ -59,10 +67,14 @@ _get_ssm_session_info_async() { else echo "Error: Failed to get SSM session info. HTTP status: $http_status" echo "Response: $session_json" + # Clean up temporary file on error + rm -f "$temp_file" exit 1 fi done + # Clean up temporary file on timeout + rm -f "$temp_file" echo "Error: Timed out after $max_retries attempts waiting for session to be ready." exit 1 } diff --git a/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts b/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts index e59b1b9dd10..f8dad504067 100644 --- a/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts +++ b/packages/core/src/awsService/sagemaker/detached-server/routes/getSessionAsync.ts @@ -50,7 +50,7 @@ export async function handleGetSessionAsync(req: IncomingMessage, res: ServerRes const refreshUrl = await store.getRefreshUrl(connectionIdentifier) const { spaceName } = parseArn(connectionIdentifier) - const url = `${refreshUrl}/${encodeURIComponent(spaceName)}?reconnect_identifier=${encodeURIComponent( + const url = `${refreshUrl}/${encodeURIComponent(spaceName)}?remote_access_token_refresh=true&reconnect_identifier=${encodeURIComponent( connectionIdentifier )}&reconnect_request_id=${encodeURIComponent(requestId)}&reconnect_callback_url=${encodeURIComponent( `http://localhost:${serverInfo.port}/refresh_token` diff --git a/packages/core/src/awsService/sagemaker/detached-server/utils.ts b/packages/core/src/awsService/sagemaker/detached-server/utils.ts index 9ac6b6b0303..de01041d4ad 100644 --- a/packages/core/src/awsService/sagemaker/detached-server/utils.ts +++ b/packages/core/src/awsService/sagemaker/detached-server/utils.ts @@ -18,6 +18,10 @@ export { open } export const mappingFilePath = join(os.homedir(), '.aws', '.sagemaker-space-profiles') const tempFilePath = `${mappingFilePath}.tmp` +// Simple file lock to prevent concurrent writes +let isWriting = false +const writeQueue: Array<() => Promise> = [] + /** * Reads the local endpoint info file (default or via env) and returns pid & port. * @throws Error if the file is missing, invalid JSON, or missing fields @@ -100,14 +104,48 @@ export async function readMapping() { } /** - * Writes the mapping to a temp file and atomically renames it to the target path. + * Processes the write queue to ensure only one write operation happens at a time. */ -export async function writeMapping(mapping: SpaceMappings) { +async function processWriteQueue() { + if (isWriting || writeQueue.length === 0) { + return + } + + isWriting = true try { - const json = JSON.stringify(mapping, undefined, 2) - await fs.writeFile(tempFilePath, json) - await fs.rename(tempFilePath, mappingFilePath) - } catch (err) { - throw new Error(`Failed to write mapping file: ${err instanceof Error ? err.message : String(err)}`) + while (writeQueue.length > 0) { + const writeOperation = writeQueue.shift()! + await writeOperation() + } + } finally { + isWriting = false } } + +/** + * Writes the mapping to a temp file and atomically renames it to the target path. + * Uses a queue to prevent race conditions when multiple requests try to write simultaneously. + */ +export async function writeMapping(mapping: SpaceMappings) { + return new Promise((resolve, reject) => { + const writeOperation = async () => { + try { + // Generate unique temp file name to avoid conflicts + const uniqueTempPath = `${tempFilePath}.${process.pid}.${Date.now()}` + + const json = JSON.stringify(mapping, undefined, 2) + await fs.writeFile(uniqueTempPath, json) + await fs.rename(uniqueTempPath, mappingFilePath) + resolve() + } catch (err) { + reject(new Error(`Failed to write mapping file: ${err instanceof Error ? err.message : String(err)}`)) + } + } + + writeQueue.push(writeOperation) + + // ProcessWriteQueue handles its own errors via individual operation callbacks + // eslint-disable-next-line @typescript-eslint/no-floating-promises + processWriteQueue() + }) +} diff --git a/packages/core/src/test/awsService/sagemaker/detached-server/utils.test.ts b/packages/core/src/test/awsService/sagemaker/detached-server/utils.test.ts index 1eeb5708d11..bc8d0a8867b 100644 --- a/packages/core/src/test/awsService/sagemaker/detached-server/utils.test.ts +++ b/packages/core/src/test/awsService/sagemaker/detached-server/utils.test.ts @@ -3,8 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ +/* eslint-disable no-restricted-imports */ import * as assert from 'assert' -import { parseArn } from '../../../../awsService/sagemaker/detached-server/utils' +import { parseArn, writeMapping, readMapping } from '../../../../awsService/sagemaker/detached-server/utils' +import { promises as fs } from 'fs' +import * as path from 'path' +import * as os from 'os' +import { SpaceMappings } from '../../../../awsService/sagemaker/types' describe('parseArn', () => { it('parses a standard SageMaker ARN with forward slash', () => { @@ -37,3 +42,71 @@ describe('parseArn', () => { assert.throws(() => parseArn(invalidArn), /Invalid SageMaker ARN format/) }) }) + +describe('writeMapping', () => { + let testDir: string + + beforeEach(async () => { + testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'sagemaker-test-')) + }) + + afterEach(async () => { + await fs.rmdir(testDir, { recursive: true }) + }) + + it('handles concurrent writes without race conditions', async () => { + const mapping1: SpaceMappings = { + localCredential: { + 'space-1': { type: 'iam', profileName: 'profile1' }, + }, + } + const mapping2: SpaceMappings = { + localCredential: { + 'space-2': { type: 'iam', profileName: 'profile2' }, + }, + } + const mapping3: SpaceMappings = { + deepLink: { + 'space-3': { + requests: { + req1: { + sessionId: 'session-456', + url: 'wss://example3.com', + token: 'token-456', + }, + }, + refreshUrl: 'https://example3.com/refresh', + }, + }, + } + + const writePromises = [writeMapping(mapping1), writeMapping(mapping2), writeMapping(mapping3)] + + await Promise.all(writePromises) + + const finalContent = await readMapping() + const possibleResults = [mapping1, mapping2, mapping3] + const isValidResult = possibleResults.some( + (expected) => JSON.stringify(finalContent) === JSON.stringify(expected) + ) + assert.strictEqual(isValidResult, true, 'Final content should match one of the written mappings') + }) + + it('queues multiple writes and processes them sequentially', async () => { + const mappings = Array.from({ length: 5 }, (_, i) => ({ + localCredential: { + [`space-${i}`]: { type: 'iam' as const, profileName: `profile-${i}` }, + }, + })) + + const writePromises = mappings.map((mapping) => writeMapping(mapping)) + + await Promise.all(writePromises) + + const finalContent = await readMapping() + assert.strictEqual(typeof finalContent, 'object', 'Final content should be a valid object') + + const isValidResult = mappings.some((mapping) => JSON.stringify(finalContent) === JSON.stringify(mapping)) + assert.strictEqual(isValidResult, true, 'Final content should match one of the written mappings') + }) +}) diff --git a/packages/toolkit/.changes/next-release/Bug Fix-c6e841d7-e0bb-474c-9540-f896746d26d4.json b/packages/toolkit/.changes/next-release/Bug Fix-c6e841d7-e0bb-474c-9540-f896746d26d4.json new file mode 100644 index 00000000000..18aea78cb6a --- /dev/null +++ b/packages/toolkit/.changes/next-release/Bug Fix-c6e841d7-e0bb-474c-9540-f896746d26d4.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "SageMaker: Resolve race condition when reconnecting from multiple remote windows." +} From ef702faf8219efdcc9f81a463cf6b7e8ac64e7c4 Mon Sep 17 00:00:00 2001 From: aws-toolkit-automation <> Date: Wed, 16 Jul 2025 20:07:49 +0000 Subject: [PATCH 08/69] Release 3.69.0 --- package-lock.json | 4 +- packages/toolkit/.changes/3.69.0.json | 46 +++++++++++++++++++ ...-25f95c85-8b96-4cbd-bc3e-da833340be06.json | 4 -- ...-2e0b8ccd-08eb-46e6-947d-27ef5701aca8.json | 4 -- ...-3d681d73-4171-44f3-a5ee-50f192a398ca.json | 4 -- ...-70bf4138-7b79-4fc1-8e6a-0637f0058da6.json | 4 -- ...-7be7d120-d44b-493d-85d1-9ab9260c958f.json | 4 -- ...-c6e841d7-e0bb-474c-9540-f896746d26d4.json | 4 -- ...-da7c8255-3962-49eb-b4e6-6091eb788bca.json | 4 -- ...-3fa7c727-cb0a-439c-9bfe-34a2a1fa99de.json | 4 -- ...-ca5ca54f-d5f4-472e-934e-9fa79d783a98.json | 4 -- ...-d97769de-7dd4-4fe6-a3ba-c951994e6231.json | 4 -- packages/toolkit/CHANGELOG.md | 13 ++++++ packages/toolkit/package.json | 2 +- 14 files changed, 62 insertions(+), 43 deletions(-) create mode 100644 packages/toolkit/.changes/3.69.0.json delete mode 100644 packages/toolkit/.changes/next-release/Bug Fix-25f95c85-8b96-4cbd-bc3e-da833340be06.json delete mode 100644 packages/toolkit/.changes/next-release/Bug Fix-2e0b8ccd-08eb-46e6-947d-27ef5701aca8.json delete mode 100644 packages/toolkit/.changes/next-release/Bug Fix-3d681d73-4171-44f3-a5ee-50f192a398ca.json delete mode 100644 packages/toolkit/.changes/next-release/Bug Fix-70bf4138-7b79-4fc1-8e6a-0637f0058da6.json delete mode 100644 packages/toolkit/.changes/next-release/Bug Fix-7be7d120-d44b-493d-85d1-9ab9260c958f.json delete mode 100644 packages/toolkit/.changes/next-release/Bug Fix-c6e841d7-e0bb-474c-9540-f896746d26d4.json delete mode 100644 packages/toolkit/.changes/next-release/Bug Fix-da7c8255-3962-49eb-b4e6-6091eb788bca.json delete mode 100644 packages/toolkit/.changes/next-release/Feature-3fa7c727-cb0a-439c-9bfe-34a2a1fa99de.json delete mode 100644 packages/toolkit/.changes/next-release/Feature-ca5ca54f-d5f4-472e-934e-9fa79d783a98.json delete mode 100644 packages/toolkit/.changes/next-release/Feature-d97769de-7dd4-4fe6-a3ba-c951994e6231.json diff --git a/package-lock.json b/package-lock.json index da7a479fbb9..19534a9724b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "prettier": "^3.3.3", "prettier-plugin-sh": "^0.14.0", "pretty-quick": "^4.0.0", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "typescript": "^5.0.4", "webpack": "^5.95.0", "webpack-cli": "^5.1.4", @@ -31678,7 +31678,7 @@ }, "packages/toolkit": { "name": "aws-toolkit-vscode", - "version": "3.69.0-SNAPSHOT", + "version": "3.69.0", "license": "Apache-2.0", "dependencies": { "aws-core-vscode": "file:../core/" diff --git a/packages/toolkit/.changes/3.69.0.json b/packages/toolkit/.changes/3.69.0.json new file mode 100644 index 00000000000..dc2d04d1582 --- /dev/null +++ b/packages/toolkit/.changes/3.69.0.json @@ -0,0 +1,46 @@ +{ + "date": "2025-07-16", + "version": "3.69.0", + "entries": [ + { + "type": "Bug Fix", + "description": "SageMaker: Enable per-region manual filtering of Spaces" + }, + { + "type": "Bug Fix", + "description": "SageMaker: Show error message when connecting remotely from a remote workspace" + }, + { + "type": "Bug Fix", + "description": "SageMaker: Prompt user to use upgraded instance type if the chosen one has insufficient memory" + }, + { + "type": "Bug Fix", + "description": "Lambda upload from directory doesn't allow selection of directory" + }, + { + "type": "Bug Fix", + "description": "Toolkit fails to recognize it's logged in when editing Lambda function" + }, + { + "type": "Bug Fix", + "description": "SageMaker: Resolve race condition when reconnecting from multiple remote windows." + }, + { + "type": "Bug Fix", + "description": "SageMaker: Resolve connection issues to SageMaker Spaces with capital letters in the name" + }, + { + "type": "Feature", + "description": "SageMaker: Add support for deep-linked Space reconnection" + }, + { + "type": "Feature", + "description": "Lambda Remote Debugging: Remote invoke configuration webview now supports attaching a debugger to directly debug your lambda function in the cloud." + }, + { + "type": "Feature", + "description": "SageMaker: Enable auto-shutdown support for Spaces" + } + ] +} \ No newline at end of file diff --git a/packages/toolkit/.changes/next-release/Bug Fix-25f95c85-8b96-4cbd-bc3e-da833340be06.json b/packages/toolkit/.changes/next-release/Bug Fix-25f95c85-8b96-4cbd-bc3e-da833340be06.json deleted file mode 100644 index aa8be24a11c..00000000000 --- a/packages/toolkit/.changes/next-release/Bug Fix-25f95c85-8b96-4cbd-bc3e-da833340be06.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "SageMaker: Enable per-region manual filtering of Spaces" -} diff --git a/packages/toolkit/.changes/next-release/Bug Fix-2e0b8ccd-08eb-46e6-947d-27ef5701aca8.json b/packages/toolkit/.changes/next-release/Bug Fix-2e0b8ccd-08eb-46e6-947d-27ef5701aca8.json deleted file mode 100644 index 14364c43d14..00000000000 --- a/packages/toolkit/.changes/next-release/Bug Fix-2e0b8ccd-08eb-46e6-947d-27ef5701aca8.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "SageMaker: Show error message when connecting remotely from a remote workspace" -} diff --git a/packages/toolkit/.changes/next-release/Bug Fix-3d681d73-4171-44f3-a5ee-50f192a398ca.json b/packages/toolkit/.changes/next-release/Bug Fix-3d681d73-4171-44f3-a5ee-50f192a398ca.json deleted file mode 100644 index b1c50349e5d..00000000000 --- a/packages/toolkit/.changes/next-release/Bug Fix-3d681d73-4171-44f3-a5ee-50f192a398ca.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "SageMaker: Prompt user to use upgraded instance type if the chosen one has insufficient memory" -} diff --git a/packages/toolkit/.changes/next-release/Bug Fix-70bf4138-7b79-4fc1-8e6a-0637f0058da6.json b/packages/toolkit/.changes/next-release/Bug Fix-70bf4138-7b79-4fc1-8e6a-0637f0058da6.json deleted file mode 100644 index c11eb175a75..00000000000 --- a/packages/toolkit/.changes/next-release/Bug Fix-70bf4138-7b79-4fc1-8e6a-0637f0058da6.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "Lambda upload from directory doesn't allow selection of directory" -} diff --git a/packages/toolkit/.changes/next-release/Bug Fix-7be7d120-d44b-493d-85d1-9ab9260c958f.json b/packages/toolkit/.changes/next-release/Bug Fix-7be7d120-d44b-493d-85d1-9ab9260c958f.json deleted file mode 100644 index da18c361957..00000000000 --- a/packages/toolkit/.changes/next-release/Bug Fix-7be7d120-d44b-493d-85d1-9ab9260c958f.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "Toolkit fails to recognize it's logged in when editing Lambda function" -} diff --git a/packages/toolkit/.changes/next-release/Bug Fix-c6e841d7-e0bb-474c-9540-f896746d26d4.json b/packages/toolkit/.changes/next-release/Bug Fix-c6e841d7-e0bb-474c-9540-f896746d26d4.json deleted file mode 100644 index 18aea78cb6a..00000000000 --- a/packages/toolkit/.changes/next-release/Bug Fix-c6e841d7-e0bb-474c-9540-f896746d26d4.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "SageMaker: Resolve race condition when reconnecting from multiple remote windows." -} diff --git a/packages/toolkit/.changes/next-release/Bug Fix-da7c8255-3962-49eb-b4e6-6091eb788bca.json b/packages/toolkit/.changes/next-release/Bug Fix-da7c8255-3962-49eb-b4e6-6091eb788bca.json deleted file mode 100644 index 3b45f594ad6..00000000000 --- a/packages/toolkit/.changes/next-release/Bug Fix-da7c8255-3962-49eb-b4e6-6091eb788bca.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "SageMaker: Resolve connection issues to SageMaker Spaces with capital letters in the name" -} diff --git a/packages/toolkit/.changes/next-release/Feature-3fa7c727-cb0a-439c-9bfe-34a2a1fa99de.json b/packages/toolkit/.changes/next-release/Feature-3fa7c727-cb0a-439c-9bfe-34a2a1fa99de.json deleted file mode 100644 index db431135a2c..00000000000 --- a/packages/toolkit/.changes/next-release/Feature-3fa7c727-cb0a-439c-9bfe-34a2a1fa99de.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Feature", - "description": "SageMaker: Add support for deep-linked Space reconnection" -} diff --git a/packages/toolkit/.changes/next-release/Feature-ca5ca54f-d5f4-472e-934e-9fa79d783a98.json b/packages/toolkit/.changes/next-release/Feature-ca5ca54f-d5f4-472e-934e-9fa79d783a98.json deleted file mode 100644 index ab791cd6caa..00000000000 --- a/packages/toolkit/.changes/next-release/Feature-ca5ca54f-d5f4-472e-934e-9fa79d783a98.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Feature", - "description": "Lambda Remote Debugging: Remote invoke configuration webview now supports attaching a debugger to directly debug your lambda function in the cloud." -} diff --git a/packages/toolkit/.changes/next-release/Feature-d97769de-7dd4-4fe6-a3ba-c951994e6231.json b/packages/toolkit/.changes/next-release/Feature-d97769de-7dd4-4fe6-a3ba-c951994e6231.json deleted file mode 100644 index 8117953da02..00000000000 --- a/packages/toolkit/.changes/next-release/Feature-d97769de-7dd4-4fe6-a3ba-c951994e6231.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Feature", - "description": "SageMaker: Enable auto-shutdown support for Spaces" -} diff --git a/packages/toolkit/CHANGELOG.md b/packages/toolkit/CHANGELOG.md index 6aa12460867..83cc14ff4e7 100644 --- a/packages/toolkit/CHANGELOG.md +++ b/packages/toolkit/CHANGELOG.md @@ -1,3 +1,16 @@ +## 3.69.0 2025-07-16 + +- **Bug Fix** SageMaker: Enable per-region manual filtering of Spaces +- **Bug Fix** SageMaker: Show error message when connecting remotely from a remote workspace +- **Bug Fix** SageMaker: Prompt user to use upgraded instance type if the chosen one has insufficient memory +- **Bug Fix** Lambda upload from directory doesn't allow selection of directory +- **Bug Fix** Toolkit fails to recognize it's logged in when editing Lambda function +- **Bug Fix** SageMaker: Resolve race condition when reconnecting from multiple remote windows. +- **Bug Fix** SageMaker: Resolve connection issues to SageMaker Spaces with capital letters in the name +- **Feature** SageMaker: Add support for deep-linked Space reconnection +- **Feature** Lambda Remote Debugging: Remote invoke configuration webview now supports attaching a debugger to directly debug your lambda function in the cloud. +- **Feature** SageMaker: Enable auto-shutdown support for Spaces + ## 3.68.0 2025-07-03 - **Bug Fix** [StepFunctions]: Cannot call TestState with variables in Workflow Studio diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index 86b32c420cd..ba2873ba24c 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -2,7 +2,7 @@ "name": "aws-toolkit-vscode", "displayName": "AWS Toolkit", "description": "Including CodeCatalyst, Infrastructure Composer, and support for Lambda, S3, CloudWatch Logs, CloudFormation, and many other services.", - "version": "3.69.0-SNAPSHOT", + "version": "3.69.0", "extensionKind": [ "workspace" ], From 9923793586d311dd058b4134e93c1ad7ebce6814 Mon Sep 17 00:00:00 2001 From: Tyrone Smith Date: Tue, 15 Jul 2025 10:27:41 -0700 Subject: [PATCH 09/69] fix(amazonq): Reduce plugin start-up latency --- packages/amazonq/src/extension.ts | 15 +++-------- .../codewhisperer/commands/basicCommands.ts | 6 +++++ .../region/regionProfileManager.ts | 2 +- packages/core/src/shared/featureConfig.ts | 25 +++++++++++++++++++ .../src/shared/utilities/resourceCache.ts | 15 +++++++++++ 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/packages/amazonq/src/extension.ts b/packages/amazonq/src/extension.ts index 9ca13136eab..53d7cd88037 100644 --- a/packages/amazonq/src/extension.ts +++ b/packages/amazonq/src/extension.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Auth, AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth' +import { AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth' import { activate as activateCodeWhisperer, shutdown as shutdownCodeWhisperer } from 'aws-core-vscode/codewhisperer' import { makeEndpointsProvider, registerGenericCommands } from 'aws-core-vscode' import { CommonAuthWebview } from 'aws-core-vscode/login' @@ -44,7 +44,6 @@ import * as vscode from 'vscode' import { registerCommands } from './commands' import { focusAmazonQPanel } from 'aws-core-vscode/codewhispererChat' import { activate as activateAmazonqLsp } from './lsp/activation' -import { activate as activateInlineCompletion } from './app/inline/activation' import { hasGlibcPatch } from './lsp/client' export const amazonQContextPrefix = 'amazonq' @@ -126,17 +125,11 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is // This contains every lsp agnostic things (auth, security scan, code scan) await activateCodeWhisperer(extContext as ExtContext) - if ( - (Experiments.instance.get('amazonqLSP', true) || Auth.instance.isInternalAmazonUser()) && - (!isAmazonLinux2() || hasGlibcPatch()) - ) { - // start the Amazon Q LSP for internal users first - // for AL2, start LSP if glibc patch is found + + if (!isAmazonLinux2() || hasGlibcPatch()) { + // Activate Amazon Q LSP for everyone unless they're using AL2 without the glibc patch await activateAmazonqLsp(context) } - if (!Experiments.instance.get('amazonqLSPInline', true)) { - await activateInlineCompletion() - } // Generic extension commands registerGenericCommands(context, amazonQContextPrefix) diff --git a/packages/core/src/codewhisperer/commands/basicCommands.ts b/packages/core/src/codewhisperer/commands/basicCommands.ts index 745fe1a45a9..efe993356bd 100644 --- a/packages/core/src/codewhisperer/commands/basicCommands.ts +++ b/packages/core/src/codewhisperer/commands/basicCommands.ts @@ -634,6 +634,12 @@ const registerToolkitApiCallbackOnce = once(() => { export const registerToolkitApiCallback = Commands.declare( { id: 'aws.amazonq.refreshConnectionCallback' }, () => async (toolkitApi?: any) => { + // Early return if already registered to avoid duplicate work + if (_toolkitApi) { + getLogger().debug('Toolkit API callback already registered, skipping') + return + } + // While the Q/CW exposes an API for the Toolkit to register callbacks on auth changes, // we need to do it manually here because the Toolkit would have been unable to call // this API if the Q/CW extension started afterwards (and this code block is running). diff --git a/packages/core/src/codewhisperer/region/regionProfileManager.ts b/packages/core/src/codewhisperer/region/regionProfileManager.ts index e463321be19..7848f21a74e 100644 --- a/packages/core/src/codewhisperer/region/regionProfileManager.ts +++ b/packages/core/src/codewhisperer/region/regionProfileManager.ts @@ -77,7 +77,7 @@ export class RegionProfileManager { result: undefined, }, }, - { timeout: 15000, interval: 1500, truthy: true } + { timeout: 15000, interval: 500, truthy: true } ) } diff --git a/packages/core/src/shared/featureConfig.ts b/packages/core/src/shared/featureConfig.ts index d7acb9657be..c7b111b3243 100644 --- a/packages/core/src/shared/featureConfig.ts +++ b/packages/core/src/shared/featureConfig.ts @@ -55,6 +55,9 @@ export const featureDefinitions = new Map([ export class FeatureConfigProvider { private featureConfigs = new Map() + private fetchPromise: Promise | undefined = undefined + private lastFetchTime = 0 + private readonly minFetchInterval = 5000 // 5 seconds minimum between fetches static #instance: FeatureConfigProvider @@ -123,6 +126,28 @@ export class FeatureConfigProvider { return } + // Debounce multiple concurrent calls + const now = performance.now() + if (this.fetchPromise && now - this.lastFetchTime < this.minFetchInterval) { + getLogger().debug('amazonq: Debouncing feature config fetch') + return this.fetchPromise + } + + if (this.fetchPromise) { + return this.fetchPromise + } + + this.lastFetchTime = now + this.fetchPromise = this._fetchFeatureConfigsInternal() + + try { + await this.fetchPromise + } finally { + this.fetchPromise = undefined + } + } + + private async _fetchFeatureConfigsInternal(): Promise { getLogger().debug('amazonq: Fetching feature configs') try { const response = await this.listFeatureEvaluations() diff --git a/packages/core/src/shared/utilities/resourceCache.ts b/packages/core/src/shared/utilities/resourceCache.ts index c0beee61cd6..a399dea66ca 100644 --- a/packages/core/src/shared/utilities/resourceCache.ts +++ b/packages/core/src/shared/utilities/resourceCache.ts @@ -60,6 +60,21 @@ export abstract class CachedResource { abstract resourceProvider(): Promise async getResource(): Promise { + // Check cache without locking first + const quickCheck = this.readCacheOrDefault() + if (quickCheck.resource.result && !quickCheck.resource.locked) { + const duration = now() - quickCheck.resource.timestamp + if (duration < this.expirationInMilli) { + logger.debug( + `cache hit (fast path), duration(%sms) is less than expiration(%sms), returning cached value: %s`, + duration, + this.expirationInMilli, + this.key + ) + return quickCheck.resource.result + } + } + const cachedValue = await this.tryLoadResourceAndLock() const resource = cachedValue?.resource From c2677006327ce550ba73dd9148884a5fa9368be2 Mon Sep 17 00:00:00 2001 From: aws-toolkit-automation <> Date: Wed, 16 Jul 2025 21:33:56 +0000 Subject: [PATCH 10/69] Update version to snapshot version: 3.70.0-SNAPSHOT --- package-lock.json | 4 ++-- packages/toolkit/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 19534a9724b..ed21305ffee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "prettier": "^3.3.3", "prettier-plugin-sh": "^0.14.0", "pretty-quick": "^4.0.0", - "ts-node": "^10.9.2", + "ts-node": "^10.9.1", "typescript": "^5.0.4", "webpack": "^5.95.0", "webpack-cli": "^5.1.4", @@ -31678,7 +31678,7 @@ }, "packages/toolkit": { "name": "aws-toolkit-vscode", - "version": "3.69.0", + "version": "3.70.0-SNAPSHOT", "license": "Apache-2.0", "dependencies": { "aws-core-vscode": "file:../core/" diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index ba2873ba24c..34fb02a8bd6 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -2,7 +2,7 @@ "name": "aws-toolkit-vscode", "displayName": "AWS Toolkit", "description": "Including CodeCatalyst, Infrastructure Composer, and support for Lambda, S3, CloudWatch Logs, CloudFormation, and many other services.", - "version": "3.69.0", + "version": "3.70.0-SNAPSHOT", "extensionKind": [ "workspace" ], From b7e849450b41846102cacab8cc9c11cccd957808 Mon Sep 17 00:00:00 2001 From: Aidan Ton Date: Wed, 16 Jul 2025 15:59:54 -0700 Subject: [PATCH 11/69] fix: should check if partialResultToken is empty for EDITS trigger on acceptance --- .../src/app/inline/recommendationService.ts | 25 ++++++++++--------- .../amazonq/src/app/inline/sessionManager.ts | 4 +-- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/amazonq/src/app/inline/recommendationService.ts b/packages/amazonq/src/app/inline/recommendationService.ts index e75477bc1b9..ddde310999f 100644 --- a/packages/amazonq/src/app/inline/recommendationService.ts +++ b/packages/amazonq/src/app/inline/recommendationService.ts @@ -114,22 +114,23 @@ export class RecommendationService { ) const isInlineEdit = result.items.some((item) => item.isInlineEdit) - if (!isInlineEdit) { - // If the suggestion is COMPLETIONS and there are more results to fetch, handle them in the background - getLogger().info( - 'Suggestion type is COMPLETIONS. Start fetching for more items if partialResultToken exists.' - ) - if (result.partialResultToken) { + + if (result.partialResultToken) { + if (!isInlineEdit) { + // If the suggestion is COMPLETIONS and there are more results to fetch, handle them in the background + getLogger().info( + 'Suggestion type is COMPLETIONS. Start fetching for more items if partialResultToken exists.' + ) this.processRemainingRequests(languageClient, request, result, token).catch((error) => { languageClient.warn(`Error when getting suggestions: ${error}`) }) + } else { + // Skip fetching for more items if the suggesion is EDITS. If it is EDITS suggestion, only fetching for more + // suggestions when the user start to accept a suggesion. + // Save editsStreakPartialResultToken for the next EDITS suggestion trigger if user accepts. + getLogger().info('Suggestion type is EDITS. Skip fetching for more items.') + this.sessionManager.updateActiveEditsStreakToken(result.partialResultToken) } - } else { - // Skip fetching for more items if the suggesion is EDITS. If it is EDITS suggestion, only fetching for more - // suggestions when the user start to accept a suggesion. - // Save editsStreakPartialResultToken for the next EDITS suggestion trigger if user accepts. - getLogger().info('Suggestion type is EDITS. Skip fetching for more items.') - this.sessionManager.updateActiveEditsStreakToken(result.partialResultToken) } } catch (error: any) { getLogger().error('Error getting recommendations: %O', error) diff --git a/packages/amazonq/src/app/inline/sessionManager.ts b/packages/amazonq/src/app/inline/sessionManager.ts index 1da02fa4cd7..3592461ea8c 100644 --- a/packages/amazonq/src/app/inline/sessionManager.ts +++ b/packages/amazonq/src/app/inline/sessionManager.ts @@ -77,8 +77,8 @@ export class SessionManager { this._acceptedSuggestionCount += 1 } - public updateActiveEditsStreakToken(partialResultToken?: number | string) { - if (!this.activeSession || !partialResultToken) { + public updateActiveEditsStreakToken(partialResultToken: number | string) { + if (!this.activeSession) { return } this.activeSession.editsStreakPartialResultToken = partialResultToken From 7ba15ce49398ed6c9cd34f43a5d320aee9a69a68 Mon Sep 17 00:00:00 2001 From: abhraina-aws Date: Wed, 16 Jul 2025 18:35:51 -0700 Subject: [PATCH 12/69] feature(amazonq): start rotating logging to disk with cleanup --- packages/amazonq/src/lsp/client.ts | 28 ++- .../amazonq/src/lsp/rotatingLogChannel.ts | 228 ++++++++++++++++++ .../src/test/rotatingLogChannel.test.ts | 164 +++++++++++++ 3 files changed, 412 insertions(+), 8 deletions(-) create mode 100644 packages/amazonq/src/lsp/rotatingLogChannel.ts create mode 100644 packages/amazonq/src/test/rotatingLogChannel.test.ts diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index 98427d17276..a4980154c27 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -8,6 +8,7 @@ import * as nls from 'vscode-nls' import { LanguageClient, LanguageClientOptions, RequestType, State } from 'vscode-languageclient' import { InlineCompletionManager } from '../app/inline/completion' import { AmazonQLspAuth, encryptionKey, notificationTypes } from './auth' +import { RotatingLogChannel } from './rotatingLogChannel' import { CreateFilesParams, DeleteFilesParams, @@ -94,6 +95,23 @@ export async function startLanguageServer( const clientId = 'amazonq' const traceServerEnabled = Settings.instance.isSet(`${clientId}.trace.server`) + + // Create custom output channel that writes to disk but sends UI output to the appropriate channel + const lspLogChannel = new RotatingLogChannel( + traceServerEnabled ? 'Amazon Q Language Server' : 'Amazon Q Logs', + extensionContext, + traceServerEnabled + ? vscode.window.createOutputChannel('Amazon Q Language Server', { log: true }) + : globals.logOutputChannel + ) + + // Add cleanup for our file output channel + toDispose.push({ + dispose: () => { + lspLogChannel.dispose() + }, + }) + let executable: string[] = [] // apply the GLIBC 2.28 path to node js runtime binary if (isSageMaker()) { @@ -191,15 +209,9 @@ export async function startLanguageServer( }, }, /** - * When the trace server is enabled it outputs a ton of log messages so: - * When trace server is enabled, logs go to a seperate "Amazon Q Language Server" output. - * Otherwise, logs go to the regular "Amazon Q Logs" channel. + * Using our RotatingLogger for all logs */ - ...(traceServerEnabled - ? {} - : { - outputChannel: globals.logOutputChannel, - }), + outputChannel: lspLogChannel, } const client = new LanguageClient( diff --git a/packages/amazonq/src/lsp/rotatingLogChannel.ts b/packages/amazonq/src/lsp/rotatingLogChannel.ts new file mode 100644 index 00000000000..24d6e110771 --- /dev/null +++ b/packages/amazonq/src/lsp/rotatingLogChannel.ts @@ -0,0 +1,228 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as path from 'path' +import * as fs from 'fs' // eslint-disable-line no-restricted-imports +import { getLogger } from 'aws-core-vscode/shared' + +export class RotatingLogChannel implements vscode.LogOutputChannel { + private fileStream: fs.WriteStream | undefined + private originalChannel: vscode.LogOutputChannel + private logger = getLogger('amazonqLsp') + private _logLevel: vscode.LogLevel = vscode.LogLevel.Info + private currentFileSize = 0 + // eslint-disable-next-line @typescript-eslint/naming-convention + private readonly MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB + // eslint-disable-next-line @typescript-eslint/naming-convention + private readonly MAX_LOG_FILES = 4 + + constructor( + public readonly name: string, + private readonly extensionContext: vscode.ExtensionContext, + outputChannel: vscode.LogOutputChannel + ) { + this.originalChannel = outputChannel + this.initFileStream() + } + + private async cleanupOldLogs(): Promise { + try { + const logDir = this.extensionContext.storageUri?.fsPath + if (!logDir) { + return + } + + // Get all log files + const files = await fs.promises.readdir(logDir) + const logFiles = files + .filter((f) => f.startsWith('amazonq-lsp-') && f.endsWith('.log')) + .map((f) => ({ + name: f, + path: path.join(logDir, f), + time: fs.statSync(path.join(logDir, f)).mtime.getTime(), + })) + .sort((a, b) => b.time - a.time) // Sort newest to oldest + + // Remove all but the most recent MAX_LOG_FILES files + for (const file of logFiles.slice(this.MAX_LOG_FILES - 1)) { + try { + await fs.promises.unlink(file.path) + this.logger.debug(`Removed old log file: ${file.path}`) + } catch (err) { + this.logger.error(`Failed to remove old log file ${file.path}: ${err}`) + } + } + } catch (err) { + this.logger.error(`Failed to cleanup old logs: ${err}`) + } + } + + private getLogFilePath(): string { + const logDir = this.extensionContext.storageUri?.fsPath + if (!logDir) { + throw new Error('No storage URI available') + } + + const timestamp = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '-').replace('Z', '') + return path.join(logDir, `amazonq-lsp-${timestamp}.log`) + } + + private async rotateLog(): Promise { + try { + // Close current stream + if (this.fileStream) { + this.fileStream.end() + } + + // Create new log file + const newLogPath = this.getLogFilePath() + this.fileStream = fs.createWriteStream(newLogPath, { flags: 'a' }) + this.currentFileSize = 0 + + // Clean up old files + await this.cleanupOldLogs() + + this.logger.info(`Created new log file: ${newLogPath}`) + } catch (err) { + this.logger.error(`Failed to rotate log file: ${err}`) + } + } + + private initFileStream() { + try { + const logDir = this.extensionContext.storageUri + if (!logDir) { + this.logger.error('Failed to get storage URI for logs') + return + } + + // Ensure directory exists + if (!fs.existsSync(logDir.fsPath)) { + fs.mkdirSync(logDir.fsPath, { recursive: true }) + } + + const logPath = this.getLogFilePath() + this.fileStream = fs.createWriteStream(logPath, { flags: 'a' }) + this.currentFileSize = 0 + this.logger.info(`Logging to file: ${logPath}`) + } catch (err) { + this.logger.error(`Failed to create log file: ${err}`) + } + } + + get logLevel(): vscode.LogLevel { + return this._logLevel + } + + get onDidChangeLogLevel(): vscode.Event { + return this.originalChannel.onDidChangeLogLevel + } + + trace(message: string, ...args: any[]): void { + this.originalChannel.trace(message, ...args) + this.writeToFile(`[TRACE] ${message}`) + } + + debug(message: string, ...args: any[]): void { + this.originalChannel.debug(message, ...args) + this.writeToFile(`[DEBUG] ${message}`) + } + + info(message: string, ...args: any[]): void { + this.originalChannel.info(message, ...args) + this.writeToFile(`[INFO] ${message}`) + } + + warn(message: string, ...args: any[]): void { + this.originalChannel.warn(message, ...args) + this.writeToFile(`[WARN] ${message}`) + } + + error(message: string | Error, ...args: any[]): void { + this.originalChannel.error(message, ...args) + this.writeToFile(`[ERROR] ${message instanceof Error ? message.stack || message.message : message}`) + } + + append(value: string): void { + this.originalChannel.append(value) + this.writeToFile(value) + } + + appendLine(value: string): void { + this.originalChannel.appendLine(value) + this.writeToFile(value + '\n') + } + + replace(value: string): void { + this.originalChannel.replace(value) + this.writeToFile(`[REPLACE] ${value}`) + } + + clear(): void { + this.originalChannel.clear() + } + + show(preserveFocus?: boolean): void + show(column?: vscode.ViewColumn, preserveFocus?: boolean): void + show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void { + if (typeof columnOrPreserveFocus === 'boolean') { + this.originalChannel.show(columnOrPreserveFocus) + } else { + this.originalChannel.show(columnOrPreserveFocus, preserveFocus) + } + } + + hide(): void { + this.originalChannel.hide() + } + + dispose(): void { + // First dispose the original channel + this.originalChannel.dispose() + + // Close our file stream if it exists + if (this.fileStream) { + this.fileStream.end() + } + + // Clean up all log files + const logDir = this.extensionContext.storageUri?.fsPath + if (logDir) { + try { + const files = fs.readdirSync(logDir) + for (const file of files) { + if (file.startsWith('amazonq-lsp-') && file.endsWith('.log')) { + fs.unlinkSync(path.join(logDir, file)) + } + } + this.logger.info('Cleaned up all log files during disposal') + } catch (err) { + this.logger.error(`Failed to cleanup log files during disposal: ${err}`) + } + } + } + + private writeToFile(content: string): void { + if (this.fileStream) { + try { + const timestamp = new Date().toISOString() + const logLine = `${timestamp} ${content}\n` + const size = Buffer.byteLength(logLine) + + // If this write would exceed max file size, rotate first + if (this.currentFileSize + size > this.MAX_FILE_SIZE) { + void this.rotateLog() + } + + this.fileStream.write(logLine) + this.currentFileSize += size + } catch (err) { + this.logger.error(`Failed to write to log file: ${err}`) + void this.rotateLog() + } + } + } +} diff --git a/packages/amazonq/src/test/rotatingLogChannel.test.ts b/packages/amazonq/src/test/rotatingLogChannel.test.ts new file mode 100644 index 00000000000..ec963ebac9f --- /dev/null +++ b/packages/amazonq/src/test/rotatingLogChannel.test.ts @@ -0,0 +1,164 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +// eslint-disable-next-line no-restricted-imports +import * as fs from 'fs' +import * as path from 'path' +import * as assert from 'assert' +import { RotatingLogChannel } from '../lsp/rotatingLogChannel' + +describe('RotatingLogChannel', () => { + let testDir: string + let mockExtensionContext: vscode.ExtensionContext + let mockOutputChannel: vscode.LogOutputChannel + let logChannel: RotatingLogChannel + + beforeEach(() => { + // Create a temp test directory + testDir = fs.mkdtempSync('amazonq-test-logs-') + + // Mock extension context + mockExtensionContext = { + storageUri: { fsPath: testDir } as vscode.Uri, + } as vscode.ExtensionContext + + // Mock output channel + mockOutputChannel = { + name: 'Test Output Channel', + append: () => {}, + appendLine: () => {}, + replace: () => {}, + clear: () => {}, + show: () => {}, + hide: () => {}, + dispose: () => {}, + trace: () => {}, + debug: () => {}, + info: () => {}, + warn: () => {}, + error: () => {}, + logLevel: vscode.LogLevel.Info, + onDidChangeLogLevel: new vscode.EventEmitter().event, + } + + // Create log channel instance + logChannel = new RotatingLogChannel('test', mockExtensionContext, mockOutputChannel) + }) + + afterEach(() => { + // Cleanup test directory + if (fs.existsSync(testDir)) { + fs.rmSync(testDir, { recursive: true, force: true }) + } + }) + + it('creates log file on initialization', () => { + const files = fs.readdirSync(testDir) + assert.strictEqual(files.length, 1) + assert.ok(files[0].startsWith('amazonq-lsp-')) + assert.ok(files[0].endsWith('.log')) + }) + + it('writes logs to file', async () => { + const testMessage = 'test log message' + logChannel.info(testMessage) + + // Allow async operations to complete + await new Promise((resolve) => setTimeout(resolve, 100)) + + const files = fs.readdirSync(testDir) + const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') + assert.ok(content.includes(testMessage)) + }) + + it('rotates files when size limit is reached', async () => { + // Write enough data to trigger rotation + const largeMessage = 'x'.repeat(1024 * 1024) // 1MB + for (let i = 0; i < 6; i++) { + // Should create at least 2 files + logChannel.info(largeMessage) + } + + // Allow async operations to complete + await new Promise((resolve) => setTimeout(resolve, 100)) + + const files = fs.readdirSync(testDir) + assert.ok(files.length > 1, 'Should have created multiple log files') + assert.ok(files.length <= 4, 'Should not exceed max file limit') + }) + + it('keeps only the specified number of files', async () => { + // Write enough data to create more than MAX_LOG_FILES + const largeMessage = 'x'.repeat(1024 * 1024) // 1MB + for (let i = 0; i < 20; i++) { + // Should trigger multiple rotations + logChannel.info(largeMessage) + } + + // Allow async operations to complete + await new Promise((resolve) => setTimeout(resolve, 100)) + + const files = fs.readdirSync(testDir) + assert.strictEqual(files.length, 4, 'Should keep exactly 4 files') + }) + + it('cleans up all files on dispose', async () => { + // Write some logs + logChannel.info('test message') + + // Allow async operations to complete + await new Promise((resolve) => setTimeout(resolve, 100)) + + // Verify files exist + assert.ok(fs.readdirSync(testDir).length > 0) + + // Dispose + logChannel.dispose() + + // Allow async operations to complete + await new Promise((resolve) => setTimeout(resolve, 100)) + + // Verify files are cleaned up + const remainingFiles = fs.readdirSync(testDir).filter((f) => f.startsWith('amazonq-lsp-') && f.endsWith('.log')) + assert.strictEqual(remainingFiles.length, 0, 'Should have no log files after disposal') + }) + + it('includes timestamps in log messages', async () => { + const testMessage = 'test message' + logChannel.info(testMessage) + + // Allow async operations to complete + await new Promise((resolve) => setTimeout(resolve, 100)) + + const files = fs.readdirSync(testDir) + const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') + + // ISO date format regex + const timestampRegex = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/ + assert.ok(timestampRegex.test(content), 'Log entry should include ISO timestamp') + }) + + it('handles different log levels correctly', async () => { + const testMessage = 'test message' + logChannel.trace(testMessage) + logChannel.debug(testMessage) + logChannel.info(testMessage) + logChannel.warn(testMessage) + logChannel.error(testMessage) + + // Allow async operations to complete + await new Promise((resolve) => setTimeout(resolve, 100)) + + const files = fs.readdirSync(testDir) + const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') + + assert.ok(content.includes('[TRACE]'), 'Should include TRACE level') + assert.ok(content.includes('[DEBUG]'), 'Should include DEBUG level') + assert.ok(content.includes('[INFO]'), 'Should include INFO level') + assert.ok(content.includes('[WARN]'), 'Should include WARN level') + assert.ok(content.includes('[ERROR]'), 'Should include ERROR level') + }) +}) From c6c5d76d6d88fbd0f5d4709ea11393b5b432569a Mon Sep 17 00:00:00 2001 From: Nitish <149117626+singhAws@users.noreply.github.com> Date: Wed, 16 Jul 2025 19:58:48 -0700 Subject: [PATCH 13/69] fix(amazonq): remove feature flag for CodeReview tool, update change logs (#7689) ## Problem - Removing feature flag for Code Review tool - Removed change logs for the above too --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- .../Feature-a0140eaf-abe8-43ae-9ea1-f0b1afcbc962.json | 4 ---- packages/amazonq/src/lsp/client.ts | 1 - 2 files changed, 5 deletions(-) delete mode 100644 packages/amazonq/.changes/next-release/Feature-a0140eaf-abe8-43ae-9ea1-f0b1afcbc962.json diff --git a/packages/amazonq/.changes/next-release/Feature-a0140eaf-abe8-43ae-9ea1-f0b1afcbc962.json b/packages/amazonq/.changes/next-release/Feature-a0140eaf-abe8-43ae-9ea1-f0b1afcbc962.json deleted file mode 100644 index 1048a3e14c0..00000000000 --- a/packages/amazonq/.changes/next-release/Feature-a0140eaf-abe8-43ae-9ea1-f0b1afcbc962.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Feature", - "description": "QCodeReview tool will update CodeIssues panel along with quick action - `/review`" -} diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index 98427d17276..e6ef1e5dd9c 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -168,7 +168,6 @@ export async function startLanguageServer( reroute: true, modelSelection: true, workspaceFilePath: vscode.workspace.workspaceFile?.fsPath, - qCodeReviewInChat: true, }, window: { notifications: true, From f07287daa0bf0460db6c2c6754322e520deaf84c Mon Sep 17 00:00:00 2001 From: aws-toolkit-automation <> Date: Thu, 17 Jul 2025 03:14:28 +0000 Subject: [PATCH 14/69] Release 1.84.0 --- package-lock.json | 4 ++-- packages/amazonq/.changes/1.84.0.json | 18 ++++++++++++++++++ ...x-45aef014-07f3-4511-a9f6-d7233077784c.json | 4 ---- ...x-91380b87-5955-4c15-b762-31e7f1c71575.json | 4 ---- ...e-9e413673-5ef6-4920-97b1-e73635f3a0f5.json | 4 ---- packages/amazonq/CHANGELOG.md | 6 ++++++ packages/amazonq/package.json | 2 +- 7 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 packages/amazonq/.changes/1.84.0.json delete mode 100644 packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json delete mode 100644 packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json delete mode 100644 packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json diff --git a/package-lock.json b/package-lock.json index ed21305ffee..68b4eba0c77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "prettier": "^3.3.3", "prettier-plugin-sh": "^0.14.0", "pretty-quick": "^4.0.0", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "typescript": "^5.0.4", "webpack": "^5.95.0", "webpack-cli": "^5.1.4", @@ -29954,7 +29954,7 @@ }, "packages/amazonq": { "name": "amazon-q-vscode", - "version": "1.84.0-SNAPSHOT", + "version": "1.84.0", "license": "Apache-2.0", "dependencies": { "aws-core-vscode": "file:../core/" diff --git a/packages/amazonq/.changes/1.84.0.json b/packages/amazonq/.changes/1.84.0.json new file mode 100644 index 00000000000..e73a685e054 --- /dev/null +++ b/packages/amazonq/.changes/1.84.0.json @@ -0,0 +1,18 @@ +{ + "date": "2025-07-17", + "version": "1.84.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Slightly delay rendering inline completion when user is typing" + }, + { + "type": "Bug Fix", + "description": "Render first response before receiving all paginated inline completion results" + }, + { + "type": "Feature", + "description": "Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json b/packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json deleted file mode 100644 index 4d45af73411..00000000000 --- a/packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "Slightly delay rendering inline completion when user is typing" -} diff --git a/packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json b/packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json deleted file mode 100644 index 72293c3b97a..00000000000 --- a/packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "Render first response before receiving all paginated inline completion results" -} diff --git a/packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json b/packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json deleted file mode 100644 index af699a24355..00000000000 --- a/packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Feature", - "description": "Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab." -} diff --git a/packages/amazonq/CHANGELOG.md b/packages/amazonq/CHANGELOG.md index 980abde9d63..ccf3fb8a215 100644 --- a/packages/amazonq/CHANGELOG.md +++ b/packages/amazonq/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.84.0 2025-07-17 + +- **Bug Fix** Slightly delay rendering inline completion when user is typing +- **Bug Fix** Render first response before receiving all paginated inline completion results +- **Feature** Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab. + ## 1.83.0 2025-07-09 - **Feature** Amazon Q /test, /doc, and /dev capabilities integrated into Agentic coding. diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index 25ab19b6ffb..ab9d02274e6 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -2,7 +2,7 @@ "name": "amazon-q-vscode", "displayName": "Amazon Q", "description": "The most capable generative AI-powered assistant for building, operating, and transforming software, with advanced capabilities for managing data and AI", - "version": "1.84.0-SNAPSHOT", + "version": "1.84.0", "extensionKind": [ "workspace" ], From 1a5e3767a0a7ec8ca08f6855ade269d5847334e6 Mon Sep 17 00:00:00 2001 From: Nitish Kumar Singh Date: Wed, 16 Jul 2025 21:27:37 -0700 Subject: [PATCH 15/69] fix(amazonq): enable qCodeReview tool feature flag --- packages/amazonq/src/lsp/client.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index e6ef1e5dd9c..98427d17276 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -168,6 +168,7 @@ export async function startLanguageServer( reroute: true, modelSelection: true, workspaceFilePath: vscode.workspace.workspaceFile?.fsPath, + qCodeReviewInChat: true, }, window: { notifications: true, From 3c76e30ade7c3c6fac8a94f7a0c9a3a9f6ff7cc3 Mon Sep 17 00:00:00 2001 From: Tyrone Smith Date: Thu, 17 Jul 2025 11:59:51 -0700 Subject: [PATCH 16/69] fix(amazonq): Increase region profiles cache expiration to 1 hour --- packages/core/src/codewhisperer/region/regionProfileManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/codewhisperer/region/regionProfileManager.ts b/packages/core/src/codewhisperer/region/regionProfileManager.ts index e463321be19..e46cf956c2b 100644 --- a/packages/core/src/codewhisperer/region/regionProfileManager.ts +++ b/packages/core/src/codewhisperer/region/regionProfileManager.ts @@ -69,7 +69,7 @@ export class RegionProfileManager { constructor(private readonly profileProvider: () => Promise) { super( 'aws.amazonq.regionProfiles.cache', - 60000, + 3600000, { resource: { locked: false, From c9c061ed2b9b1df279fcd567915a90ae45fedd3a Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Thu, 17 Jul 2025 12:13:39 -0700 Subject: [PATCH 17/69] fix(amazonq): handle suppress single finding in agentic reviewer --- packages/amazonq/src/lsp/chat/messages.ts | 17 +++++++++++++---- packages/core/src/shared/utilities/index.ts | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index 9841c7edee9..f869bbe0da3 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -95,6 +95,7 @@ import { decryptResponse, encryptRequest } from '../encryption' import { getCursorState } from '../utils' import { focusAmazonQPanel } from './commands' import { ChatMessage } from '@aws/language-server-runtimes/server-interface' +import { CommentUtils } from 'aws-core-vscode/utils' export function registerActiveEditorChangeListener(languageClient: LanguageClient) { let debounceTimer: NodeJS.Timeout | undefined @@ -701,7 +702,7 @@ async function handleCompleteResult( ) { const decryptedMessage = await decryptResponse(result, encryptionKey) - handleSecurityFindings(decryptedMessage, languageClient) + await handleSecurityFindings(decryptedMessage, languageClient) void provider.webview?.postMessage({ command: chatRequestType.method, @@ -716,10 +717,10 @@ async function handleCompleteResult( disposable.dispose() } -function handleSecurityFindings( +async function handleSecurityFindings( decryptedMessage: { additionalMessages?: ChatMessage[] }, languageClient: LanguageClient -): void { +): Promise { if (decryptedMessage.additionalMessages === undefined || decryptedMessage.additionalMessages.length === 0) { return } @@ -730,10 +731,18 @@ function handleSecurityFindings( try { const aggregatedCodeScanIssues: AggregatedCodeScanIssue[] = JSON.parse(message.body) for (const aggregatedCodeScanIssue of aggregatedCodeScanIssues) { + const document = await vscode.workspace.openTextDocument(aggregatedCodeScanIssue.filePath) for (const issue of aggregatedCodeScanIssue.issues) { - issue.visible = !CodeWhispererSettings.instance + const isIssueTitleIgnored = CodeWhispererSettings.instance .getIgnoredSecurityIssues() .includes(issue.title) + const isSingleIssueIgnored = CommentUtils.detectCommentAboveLine( + document, + issue.startLine, + CodeWhispererConstants.amazonqIgnoreNextLine + ) + + issue.visible = !isIssueTitleIgnored && !isSingleIssueIgnored } } initSecurityScanRender(aggregatedCodeScanIssues, undefined, CodeAnalysisScope.PROJECT) diff --git a/packages/core/src/shared/utilities/index.ts b/packages/core/src/shared/utilities/index.ts index ecf753090ca..18d86da4d55 100644 --- a/packages/core/src/shared/utilities/index.ts +++ b/packages/core/src/shared/utilities/index.ts @@ -7,3 +7,4 @@ export { isExtensionInstalled, isExtensionActive } from './vsCodeUtils' export { VSCODE_EXTENSION_ID } from '../extensions' export * from './functionUtils' export * as messageUtils from './messages' +export * as CommentUtils from './commentUtils' From c9a3e9e54551e79a70ded448fdf8750cbec927b5 Mon Sep 17 00:00:00 2001 From: mkovelam Date: Thu, 17 Jul 2025 12:16:58 -0700 Subject: [PATCH 18/69] fix(amazonq): changed the icon for security issue hover fix option to keep it consistent in all places --- .../src/codewhisperer/service/securityIssueHoverProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts index c907f99abe3..bb9fe2cafa4 100644 --- a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts +++ b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts @@ -90,7 +90,7 @@ export class SecurityIssueHoverProvider implements vscode.HoverProvider { const generateFixCommand = this._getCommandMarkdown( 'aws.amazonq.generateFix', [issue, filePath], - 'comment', + 'wrench', 'Fix', 'Fix with Amazon Q' ) From 9bb3be3f07dfb0728bf62cdcffad53ab500060e4 Mon Sep 17 00:00:00 2001 From: mkovelam Date: Thu, 17 Jul 2025 12:55:34 -0700 Subject: [PATCH 19/69] fix(core): fixing Fix icon failed unit test --- .../securityIssueHoverProvider.test.ts | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts b/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts index 9c1bb751a35..7709eed10fe 100644 --- a/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts +++ b/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts @@ -21,17 +21,41 @@ describe('securityIssueHoverProvider', () => { token = new vscode.CancellationTokenSource() }) - function buildCommandLink(command: string, args: any[], label: string, tooltip: string): string { - return `[$(${command.includes('ignore') ? 'error' : 'comment'}) ${label}](command:${command}?${encodeURIComponent(JSON.stringify(args))} '${tooltip}')` + function buildCommandLink( + command: string, + commandIcon: string, + args: any[], + label: string, + tooltip: string + ): string { + return `[$(${commandIcon}) ${label}](command:${command}?${encodeURIComponent(JSON.stringify(args))} '${tooltip}')` } function buildExpectedContent(issue: any, fileName: string, description: string, severity?: string): string { const severityBadge = severity ? ` ![${severity}](severity-${severity.toLowerCase()}.svg)` : ' ' const commands = [ - buildCommandLink('aws.amazonq.explainIssue', [issue, fileName], 'Explain', 'Explain with Amazon Q'), - buildCommandLink('aws.amazonq.generateFix', [issue, fileName], 'Fix', 'Fix with Amazon Q'), - buildCommandLink('aws.amazonq.security.ignore', [issue, fileName, 'hover'], 'Ignore', 'Ignore Issue'), - buildCommandLink('aws.amazonq.security.ignoreAll', [issue, 'hover'], 'Ignore All', 'Ignore Similar Issues'), + buildCommandLink( + 'aws.amazonq.explainIssue', + 'comment', + [issue, fileName], + 'Explain', + 'Explain with Amazon Q' + ), + buildCommandLink('aws.amazonq.generateFix', 'wrench', [issue, fileName], 'Fix', 'Fix with Amazon Q'), + buildCommandLink( + 'aws.amazonq.security.ignore', + 'error', + [issue, fileName, 'hover'], + 'Ignore', + 'Ignore Issue' + ), + buildCommandLink( + 'aws.amazonq.security.ignoreAll', + 'error', + [issue, 'hover'], + 'Ignore All', + 'Ignore Similar Issues' + ), ] return `## title${severityBadge}\n${description}\n\n${commands.join('\n | ')}\n` } From 4f9da7fe561e66d1fca52d0d23f48253713f8341 Mon Sep 17 00:00:00 2001 From: aws-toolkit-automation <> Date: Thu, 17 Jul 2025 20:28:11 +0000 Subject: [PATCH 20/69] Update version to snapshot version: 1.85.0-SNAPSHOT --- package-lock.json | 4 ++-- packages/amazonq/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 68b4eba0c77..e2b2ebb5920 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "prettier": "^3.3.3", "prettier-plugin-sh": "^0.14.0", "pretty-quick": "^4.0.0", - "ts-node": "^10.9.2", + "ts-node": "^10.9.1", "typescript": "^5.0.4", "webpack": "^5.95.0", "webpack-cli": "^5.1.4", @@ -29954,7 +29954,7 @@ }, "packages/amazonq": { "name": "amazon-q-vscode", - "version": "1.84.0", + "version": "1.85.0-SNAPSHOT", "license": "Apache-2.0", "dependencies": { "aws-core-vscode": "file:../core/" diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index ab9d02274e6..fd83354aca8 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -2,7 +2,7 @@ "name": "amazon-q-vscode", "displayName": "Amazon Q", "description": "The most capable generative AI-powered assistant for building, operating, and transforming software, with advanced capabilities for managing data and AI", - "version": "1.84.0", + "version": "1.85.0-SNAPSHOT", "extensionKind": [ "workspace" ], From a3c1c03512ba2261539d6afb52add29428f586a1 Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Thu, 17 Jul 2025 14:20:16 -0700 Subject: [PATCH 21/69] fix(amazonq): skip edit suggestion if applyDiff fail (#7693) ## Problem applyDiff may fail and the consequence is the `newCode` to update become empty string, thus deleting all users' code. ## Before https://github.com/user-attachments/assets/6524bae2-1374-452d-bb4e-3ec6f865c258 ## After https://github.com/user-attachments/assets/75d5ed7a-6940-4432-a8d9-73c485afb2c3 ## Solution --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- .../amazonq/src/app/inline/EditRendering/imageRenderer.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts b/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts index 9af3878ef82..195879ff779 100644 --- a/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts +++ b/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts @@ -29,6 +29,12 @@ export async function showEdits( const { svgImage, startLine, newCode, origionalCodeHighlightRange } = await svgGenerationService.generateDiffSvg(currentFile, item.insertText as string) + // TODO: To investigate why it fails and patch [generateDiffSvg] + if (newCode.length === 0) { + getLogger('nextEditPrediction').warn('not able to apply provided edit suggestion, skip rendering') + return + } + if (svgImage) { // display the SVG image await displaySvgDecoration( From df9a02bfe94a2fdf4a198755a127e2839bd35497 Mon Sep 17 00:00:00 2001 From: Na Yue Date: Thu, 17 Jul 2025 14:20:56 -0700 Subject: [PATCH 22/69] fix(test): add unit test for auth activation initialize method (#7679) ## Problem This pr: https://github.com/aws/aws-toolkit-vscode/pull/7670 didn't been covered by the unit test ## Solution Add unit test for the activation initialize method. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/core/src/auth/activation.ts | 2 +- .../core/src/test/auth/activation.test.ts | 146 ++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/test/auth/activation.test.ts diff --git a/packages/core/src/auth/activation.ts b/packages/core/src/auth/activation.ts index 5c48124c468..8305610dff7 100644 --- a/packages/core/src/auth/activation.ts +++ b/packages/core/src/auth/activation.ts @@ -12,7 +12,7 @@ import { isAmazonQ, isSageMaker } from '../shared/extensionUtilities' import { getLogger } from '../shared/logger/logger' import { getErrorMsg } from '../shared/errors' -interface SagemakerCookie { +export interface SagemakerCookie { authMode?: 'Sso' | 'Iam' } diff --git a/packages/core/src/test/auth/activation.test.ts b/packages/core/src/test/auth/activation.test.ts new file mode 100644 index 00000000000..f203033acba --- /dev/null +++ b/packages/core/src/test/auth/activation.test.ts @@ -0,0 +1,146 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as sinon from 'sinon' +import assert from 'assert' +import { initialize, SagemakerCookie } from '../../auth/activation' +import { LoginManager } from '../../auth/deprecated/loginManager' +import * as extensionUtilities from '../../shared/extensionUtilities' +import * as authUtils from '../../auth/utils' +import * as errors from '../../shared/errors' + +describe('auth/activation', function () { + let sandbox: sinon.SinonSandbox + let mockLoginManager: LoginManager + let executeCommandStub: sinon.SinonStub + let isAmazonQStub: sinon.SinonStub + let isSageMakerStub: sinon.SinonStub + let initializeCredentialsProviderManagerStub: sinon.SinonStub + let getErrorMsgStub: sinon.SinonStub + let mockLogger: any + + beforeEach(function () { + sandbox = sinon.createSandbox() + + // Create mocks + mockLoginManager = { + login: sandbox.stub(), + logout: sandbox.stub(), + } as any + + mockLogger = { + warn: sandbox.stub(), + info: sandbox.stub(), + error: sandbox.stub(), + debug: sandbox.stub(), + } + + // Stub external dependencies + executeCommandStub = sandbox.stub(vscode.commands, 'executeCommand') + isAmazonQStub = sandbox.stub(extensionUtilities, 'isAmazonQ') + isSageMakerStub = sandbox.stub(extensionUtilities, 'isSageMaker') + initializeCredentialsProviderManagerStub = sandbox.stub(authUtils, 'initializeCredentialsProviderManager') + getErrorMsgStub = sandbox.stub(errors, 'getErrorMsg') + }) + + afterEach(function () { + sandbox.restore() + }) + + describe('initialize', function () { + it('should not execute sagemaker.parseCookies when not in AmazonQ and SageMaker environment', async function () { + isAmazonQStub.returns(false) + isSageMakerStub.returns(false) + + await initialize(mockLoginManager) + + assert.ok(!executeCommandStub.called) + assert.ok(!initializeCredentialsProviderManagerStub.called) + }) + + it('should not execute sagemaker.parseCookies when only in AmazonQ environment', async function () { + isAmazonQStub.returns(true) + isSageMakerStub.returns(false) + + await initialize(mockLoginManager) + + assert.ok(!executeCommandStub.called) + assert.ok(!initializeCredentialsProviderManagerStub.called) + }) + + it('should not execute sagemaker.parseCookies when only in SageMaker environment', async function () { + isAmazonQStub.returns(false) + isSageMakerStub.returns(true) + + await initialize(mockLoginManager) + + assert.ok(!executeCommandStub.called) + assert.ok(!initializeCredentialsProviderManagerStub.called) + }) + + it('should execute sagemaker.parseCookies when in both AmazonQ and SageMaker environment', async function () { + isAmazonQStub.returns(true) + isSageMakerStub.returns(true) + executeCommandStub.withArgs('sagemaker.parseCookies').resolves({ authMode: 'Sso' } as SagemakerCookie) + + await initialize(mockLoginManager) + + assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) + assert.ok(!initializeCredentialsProviderManagerStub.called) + }) + + it('should initialize credentials provider manager when authMode is not Sso', async function () { + isAmazonQStub.returns(true) + isSageMakerStub.returns(true) + executeCommandStub.withArgs('sagemaker.parseCookies').resolves({ authMode: 'Iam' } as SagemakerCookie) + + await initialize(mockLoginManager) + + assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) + assert.ok(initializeCredentialsProviderManagerStub.calledOnce) + }) + + it('should initialize credentials provider manager when authMode is undefined', async function () { + isAmazonQStub.returns(true) + isSageMakerStub.returns(true) + executeCommandStub.withArgs('sagemaker.parseCookies').resolves({} as SagemakerCookie) + + await initialize(mockLoginManager) + + assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) + assert.ok(initializeCredentialsProviderManagerStub.calledOnce) + }) + + it('should warn and not throw when sagemaker.parseCookies command is not found', async function () { + isAmazonQStub.returns(true) + isSageMakerStub.returns(true) + const error = new Error("command 'sagemaker.parseCookies' not found") + executeCommandStub.withArgs('sagemaker.parseCookies').rejects(error) + getErrorMsgStub.returns("command 'sagemaker.parseCookies' not found") + + await initialize(mockLoginManager) + + assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) + assert.ok(getErrorMsgStub.calledOnceWith(error)) + assert.ok(!initializeCredentialsProviderManagerStub.called) + }) + + it('should throw when sagemaker.parseCookies fails with non-command-not-found error', async function () { + isAmazonQStub.returns(true) + isSageMakerStub.returns(true) + const error = new Error('Some other error') + executeCommandStub.withArgs('sagemaker.parseCookies').rejects(error) + getErrorMsgStub.returns('Some other error') + + await assert.rejects(initialize(mockLoginManager), /Some other error/) + + assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) + assert.ok(getErrorMsgStub.calledOnceWith(error)) + assert.ok(!mockLogger.warn.called) + assert.ok(!initializeCredentialsProviderManagerStub.called) + }) + }) +}) From f2e94039fc68b4def04073f2820878a70356df16 Mon Sep 17 00:00:00 2001 From: Lei Gao <97199248+leigaol@users.noreply.github.com> Date: Thu, 17 Jul 2025 14:34:15 -0700 Subject: [PATCH 23/69] fix(amazonq): Use document change event for auto trigger classifier input (#7697) ## Problem The auto trigger classifier needs the entire document change event as input to correctly predict whether to make auto trigger or not. This code path was lost when we migrate inline completion to Flare (language server). ## Solution Implement the IDE side changes for below PRs: https://github.com/aws/language-server-runtimes/pull/618 https://github.com/aws/language-servers/pull/1912 https://github.com/aws/language-servers/pull/1914/files --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- package-lock.json | 18 +++++++++--------- ...x-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json | 4 ++++ packages/amazonq/src/app/inline/completion.ts | 3 ++- .../src/app/inline/recommendationService.ts | 15 ++++++++++++++- .../apps/inline/recommendationService.test.ts | 2 ++ packages/core/package.json | 4 ++-- 6 files changed, 33 insertions(+), 13 deletions(-) create mode 100644 packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json diff --git a/package-lock.json b/package-lock.json index e2b2ebb5920..6e8baa4a894 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15044,13 +15044,13 @@ } }, "node_modules/@aws/language-server-runtimes": { - "version": "0.2.102", - "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes/-/language-server-runtimes-0.2.102.tgz", - "integrity": "sha512-O68zmXClLP6mtKxh0fzGKYW3MwgFCTkAgL32WKzOWLwD6gMc5CaVRrNsZ2cabkAudf2laTeWeSDZJZsiQ0hCfA==", + "version": "0.2.111", + "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes/-/language-server-runtimes-0.2.111.tgz", + "integrity": "sha512-eIHKzWkLTTb3qUCeT2nIrpP99dEv/OiUOcPB00MNCsOPWBBO/IoZhfGRNrE8+stgZMQkKLFH2ZYxn3ByB6OsCQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws/language-server-runtimes-types": "^0.1.43", + "@aws/language-server-runtimes-types": "^0.1.47", "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.200.0", "@opentelemetry/core": "^2.0.0", @@ -15077,9 +15077,9 @@ } }, "node_modules/@aws/language-server-runtimes-types": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes-types/-/language-server-runtimes-types-0.1.43.tgz", - "integrity": "sha512-qXaAGkiJ1hldF+Ynu6ZBXS18s47UOnbZEHxKiGRrBlBX2L75ih/4yasj8ITgshqS5Kx5JMntu+8vpc0CkGV6jA==", + "version": "0.1.47", + "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes-types/-/language-server-runtimes-types-0.1.47.tgz", + "integrity": "sha512-l5dOdx/MR3SO0HYXkSL9fcR05f4Aw7qRMuASMdWOK93LOSZeANPVOGIWblRnoJejfYiPXcufCFyjLnGpATExag==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -30063,8 +30063,8 @@ "@aws-sdk/types": "^3.13.1", "@aws/chat-client": "^0.1.4", "@aws/chat-client-ui-types": "^0.1.47", - "@aws/language-server-runtimes": "^0.2.102", - "@aws/language-server-runtimes-types": "^0.1.43", + "@aws/language-server-runtimes": "^0.2.111", + "@aws/language-server-runtimes-types": "^0.1.47", "@cspotcode/source-map-support": "^0.8.1", "@sinonjs/fake-timers": "^10.0.2", "@types/adm-zip": "^0.4.34", diff --git a/packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json b/packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json new file mode 100644 index 00000000000..f2234549a0d --- /dev/null +++ b/packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Use documentChangeEvent as auto trigger condition" +} diff --git a/packages/amazonq/src/app/inline/completion.ts b/packages/amazonq/src/app/inline/completion.ts index 360be53e67a..f6e080453f0 100644 --- a/packages/amazonq/src/app/inline/completion.ts +++ b/packages/amazonq/src/app/inline/completion.ts @@ -335,7 +335,8 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem context, token, isAutoTrigger, - getAllRecommendationsOptions + getAllRecommendationsOptions, + this.documentEventListener.getLastDocumentChangeEvent(document.uri.fsPath)?.event ) // get active item from session for displaying const items = this.sessionManager.getActiveRecommendation() diff --git a/packages/amazonq/src/app/inline/recommendationService.ts b/packages/amazonq/src/app/inline/recommendationService.ts index ddde310999f..1329c68a51c 100644 --- a/packages/amazonq/src/app/inline/recommendationService.ts +++ b/packages/amazonq/src/app/inline/recommendationService.ts @@ -2,10 +2,12 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ +import * as vscode from 'vscode' import { InlineCompletionListWithReferences, InlineCompletionWithReferencesParams, inlineCompletionWithReferencesRequestType, + TextDocumentContentChangeEvent, } from '@aws/language-server-runtimes/protocol' import { CancellationToken, InlineCompletionContext, Position, TextDocument } from 'vscode' import { LanguageClient } from 'vscode-languageclient' @@ -40,10 +42,20 @@ export class RecommendationService { context: InlineCompletionContext, token: CancellationToken, isAutoTrigger: boolean, - options: GetAllRecommendationsOptions = { emitTelemetry: true, showUi: true } + options: GetAllRecommendationsOptions = { emitTelemetry: true, showUi: true }, + documentChangeEvent?: vscode.TextDocumentChangeEvent ) { // Record that a regular request is being made this.cursorUpdateRecorder?.recordCompletionRequest() + const documentChangeParams = documentChangeEvent + ? { + textDocument: { + uri: document.uri.toString(), + version: document.version, + }, + contentChanges: documentChangeEvent.contentChanges.map((x) => x as TextDocumentContentChangeEvent), + } + : undefined let request: InlineCompletionWithReferencesParams = { textDocument: { @@ -51,6 +63,7 @@ export class RecommendationService { }, position, context, + documentChangeParams: documentChangeParams, } if (options.editsStreakToken) { request = { ...request, partialResultToken: options.editsStreakToken } diff --git a/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts b/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts index 744fcc63c53..54eea8347c5 100644 --- a/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts +++ b/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts @@ -146,6 +146,7 @@ describe('RecommendationService', () => { }, position: mockPosition, context: mockContext, + documentChangeParams: undefined, }) // Verify session management @@ -187,6 +188,7 @@ describe('RecommendationService', () => { }, position: mockPosition, context: mockContext, + documentChangeParams: undefined, } const secondRequestArgs = sendRequestStub.secondCall.args[1] assert.deepStrictEqual(firstRequestArgs, expectedRequestArgs) diff --git a/packages/core/package.json b/packages/core/package.json index 6f8d27ef4dc..d446a1bdf41 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -471,8 +471,8 @@ "@aws-sdk/types": "^3.13.1", "@aws/chat-client": "^0.1.4", "@aws/chat-client-ui-types": "^0.1.47", - "@aws/language-server-runtimes": "^0.2.102", - "@aws/language-server-runtimes-types": "^0.1.43", + "@aws/language-server-runtimes": "^0.2.111", + "@aws/language-server-runtimes-types": "^0.1.47", "@cspotcode/source-map-support": "^0.8.1", "@sinonjs/fake-timers": "^10.0.2", "@types/adm-zip": "^0.4.34", From 477b71af6c4418fb0de7404fd186100c18615577 Mon Sep 17 00:00:00 2001 From: Lei Gao <97199248+leigaol@users.noreply.github.com> Date: Thu, 17 Jul 2025 15:53:22 -0700 Subject: [PATCH 24/69] fix(amazonq): Let Enter invoke auto completion more consistently (#7700) ## Problem The VS Code provideInlineCompletionCallback may not trigger when Enter is pressed, especially in Python files ## Solution manually make this trigger. In case of duplicate, the provideInlineCompletionCallback is already debounced --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- ...-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json | 4 ++++ packages/amazonq/src/app/inline/completion.ts | 12 ++++++------ .../src/app/inline/documentEventListener.ts | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json diff --git a/packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json b/packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json new file mode 100644 index 00000000000..1a9e5c32e6d --- /dev/null +++ b/packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Let Enter invoke auto completion more consistently" +} diff --git a/packages/amazonq/src/app/inline/completion.ts b/packages/amazonq/src/app/inline/completion.ts index f6e080453f0..9020deac824 100644 --- a/packages/amazonq/src/app/inline/completion.ts +++ b/packages/amazonq/src/app/inline/completion.ts @@ -241,6 +241,12 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem return [] } + const isAutoTrigger = context.triggerKind === InlineCompletionTriggerKind.Automatic + if (isAutoTrigger && !CodeSuggestionsState.instance.isSuggestionsEnabled()) { + // return early when suggestions are disabled with auto trigger + return [] + } + // yield event loop to let the document listen catch updates await sleep(1) // prevent user deletion invoking auto trigger @@ -254,12 +260,6 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem try { const t0 = performance.now() vsCodeState.isRecommendationsActive = true - const isAutoTrigger = context.triggerKind === InlineCompletionTriggerKind.Automatic - if (isAutoTrigger && !CodeSuggestionsState.instance.isSuggestionsEnabled()) { - // return early when suggestions are disabled with auto trigger - return [] - } - // handling previous session const prevSession = this.sessionManager.getActiveSession() const prevSessionId = prevSession?.sessionId diff --git a/packages/amazonq/src/app/inline/documentEventListener.ts b/packages/amazonq/src/app/inline/documentEventListener.ts index 4e60b595ce2..36f65dc7331 100644 --- a/packages/amazonq/src/app/inline/documentEventListener.ts +++ b/packages/amazonq/src/app/inline/documentEventListener.ts @@ -21,6 +21,11 @@ export class DocumentEventListener { this.lastDocumentChangeEventMap.clear() } this.lastDocumentChangeEventMap.set(e.document.uri.fsPath, { event: e, timestamp: performance.now() }) + // The VS Code provideInlineCompletionCallback may not trigger when Enter is pressed, especially in Python files + // manually make this trigger. In case of duplicate, the provideInlineCompletionCallback is already debounced + if (this.isEnter(e) && vscode.window.activeTextEditor) { + void vscode.commands.executeCommand('editor.action.inlineSuggest.trigger') + } } }) } @@ -47,4 +52,18 @@ export class DocumentEventListener { this.documentChangeListener.dispose() } } + + private isEnter(e: vscode.TextDocumentChangeEvent): boolean { + if (e.contentChanges.length !== 1) { + return false + } + const str = e.contentChanges[0].text + if (str.length === 0) { + return false + } + return ( + (str.startsWith('\r\n') && str.substring(2).trim() === '') || + (str[0] === '\n' && str.substring(1).trim() === '') + ) + } } From 9b51191213e508bcb17a70073913b9124d54556f Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Thu, 17 Jul 2025 16:42:56 -0700 Subject: [PATCH 25/69] fix(amazonq): rename QCodeReview tool to CodeReview --- packages/amazonq/src/lsp/chat/messages.ts | 2 +- packages/amazonq/src/lsp/client.ts | 2 +- packages/core/src/codewhisperer/models/constants.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index f869bbe0da3..607c3d7bdc0 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -671,7 +671,7 @@ async function handlePartialResult( ) { const decryptedMessage = await decryptResponse(partialResult, encryptionKey) - // This is to filter out the message containing findings from qCodeReview tool to update CodeIssues panel + // This is to filter out the message containing findings from CodeReview tool to update CodeIssues panel decryptedMessage.additionalMessages = decryptedMessage.additionalMessages?.filter( (message) => !(message.messageId !== undefined && message.messageId.endsWith(CodeWhispererConstants.findingsSuffix)) diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index 98427d17276..5799a87f748 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -168,7 +168,7 @@ export async function startLanguageServer( reroute: true, modelSelection: true, workspaceFilePath: vscode.workspace.workspaceFile?.fsPath, - qCodeReviewInChat: true, + CodeReviewInChat: false, }, window: { notifications: true, diff --git a/packages/core/src/codewhisperer/models/constants.ts b/packages/core/src/codewhisperer/models/constants.ts index b0403e4fd3c..4a11d6e98b2 100644 --- a/packages/core/src/codewhisperer/models/constants.ts +++ b/packages/core/src/codewhisperer/models/constants.ts @@ -907,4 +907,4 @@ export const predictionTrackerDefaultConfig = { maxSupplementalContext: 15, } -export const findingsSuffix = '_qCodeReviewFindings' +export const findingsSuffix = '_CodeReviewFindings' From 5dbbbd7e3d09623dda58ec97f6288bc8bfd72dd3 Mon Sep 17 00:00:00 2001 From: abhraina-aws Date: Thu, 17 Jul 2025 17:08:23 -0700 Subject: [PATCH 26/69] feat(amazonq): added logs of toolkit to the same disk place too --- packages/amazonq/src/extension.ts | 10 +++++- .../amazonq/src/lsp/rotatingLogChannel.ts | 31 +++++++++++++++---- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/packages/amazonq/src/extension.ts b/packages/amazonq/src/extension.ts index 53d7cd88037..1e26724ff61 100644 --- a/packages/amazonq/src/extension.ts +++ b/packages/amazonq/src/extension.ts @@ -45,6 +45,7 @@ import { registerCommands } from './commands' import { focusAmazonQPanel } from 'aws-core-vscode/codewhispererChat' import { activate as activateAmazonqLsp } from './lsp/activation' import { hasGlibcPatch } from './lsp/client' +import { RotatingLogChannel } from './lsp/rotatingLogChannel' export const amazonQContextPrefix = 'amazonq' @@ -103,7 +104,12 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is globals.manifestPaths.endpoints = context.asAbsolutePath(join('resources', 'endpoints.json')) globals.regionProvider = RegionProvider.fromEndpointsProvider(makeEndpointsProvider()) - const qLogChannel = vscode.window.createOutputChannel('Amazon Q Logs', { log: true }) + // Create rotating log channel for all Amazon Q logs + const qLogChannel = new RotatingLogChannel( + 'Amazon Q Logs', + context, + vscode.window.createOutputChannel('Amazon Q Logs', { log: true }) + ) await activateLogger(context, amazonQContextPrefix, qLogChannel) globals.logOutputChannel = qLogChannel globals.loginManager = new LoginManager(globals.awsContext, new CredentialsStore()) @@ -112,6 +118,8 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is getLogger().error('fs.init: invalid env vars found: %O', homeDirLogs) } + getLogger().info('Rotating logger has been setup') + await activateTelemetry(context, globals.awsContext, Settings.instance, 'Amazon Q For VS Code') await initializeAuth(globals.loginManager) diff --git a/packages/amazonq/src/lsp/rotatingLogChannel.ts b/packages/amazonq/src/lsp/rotatingLogChannel.ts index 24d6e110771..a9ee36ed30c 100644 --- a/packages/amazonq/src/lsp/rotatingLogChannel.ts +++ b/packages/amazonq/src/lsp/rotatingLogChannel.ts @@ -18,6 +18,12 @@ export class RotatingLogChannel implements vscode.LogOutputChannel { private readonly MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB // eslint-disable-next-line @typescript-eslint/naming-convention private readonly MAX_LOG_FILES = 4 + private static currentLogPath: string | undefined + + private static generateNewLogPath(logDir: string): string { + const timestamp = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '-').replace('Z', '') + return path.join(logDir, `amazonq-lsp-${timestamp}.log`) + } constructor( public readonly name: string, @@ -61,13 +67,19 @@ export class RotatingLogChannel implements vscode.LogOutputChannel { } private getLogFilePath(): string { + // If we already have a path, reuse it + if (RotatingLogChannel.currentLogPath) { + return RotatingLogChannel.currentLogPath + } + const logDir = this.extensionContext.storageUri?.fsPath if (!logDir) { throw new Error('No storage URI available') } - const timestamp = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '-').replace('Z', '') - return path.join(logDir, `amazonq-lsp-${timestamp}.log`) + // Generate initial path + RotatingLogChannel.currentLogPath = RotatingLogChannel.generateNewLogPath(logDir) + return RotatingLogChannel.currentLogPath } private async rotateLog(): Promise { @@ -77,15 +89,22 @@ export class RotatingLogChannel implements vscode.LogOutputChannel { this.fileStream.end() } - // Create new log file - const newLogPath = this.getLogFilePath() - this.fileStream = fs.createWriteStream(newLogPath, { flags: 'a' }) + const logDir = this.extensionContext.storageUri?.fsPath + if (!logDir) { + throw new Error('No storage URI available') + } + + // Generate new path directly + RotatingLogChannel.currentLogPath = RotatingLogChannel.generateNewLogPath(logDir) + + // Create new log file with new path + this.fileStream = fs.createWriteStream(RotatingLogChannel.currentLogPath, { flags: 'a' }) this.currentFileSize = 0 // Clean up old files await this.cleanupOldLogs() - this.logger.info(`Created new log file: ${newLogPath}`) + this.logger.info(`Created new log file: ${RotatingLogChannel.currentLogPath}`) } catch (err) { this.logger.error(`Failed to rotate log file: ${err}`) } From dfdf3772146f37061963983341cc3f29d4500db3 Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Thu, 17 Jul 2025 17:24:12 -0700 Subject: [PATCH 27/69] fix(amazonq): fix issue with casing --- packages/amazonq/src/lsp/client.ts | 2 +- packages/core/src/codewhisperer/models/constants.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index 5799a87f748..ce83938d158 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -168,7 +168,7 @@ export async function startLanguageServer( reroute: true, modelSelection: true, workspaceFilePath: vscode.workspace.workspaceFile?.fsPath, - CodeReviewInChat: false, + codeReviewInChat: false, }, window: { notifications: true, diff --git a/packages/core/src/codewhisperer/models/constants.ts b/packages/core/src/codewhisperer/models/constants.ts index 4a11d6e98b2..9e1eb2b7f94 100644 --- a/packages/core/src/codewhisperer/models/constants.ts +++ b/packages/core/src/codewhisperer/models/constants.ts @@ -907,4 +907,4 @@ export const predictionTrackerDefaultConfig = { maxSupplementalContext: 15, } -export const findingsSuffix = '_CodeReviewFindings' +export const findingsSuffix = '_codeReviewFindings' From 34e7f1bea5f84e6647077843cf6d885fac966597 Mon Sep 17 00:00:00 2001 From: Reed Hamilton Date: Fri, 18 Jul 2025 12:01:09 -0700 Subject: [PATCH 28/69] Make sso quickpick option display profiles --- packages/core/src/auth/utils.ts | 61 ++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/packages/core/src/auth/utils.ts b/packages/core/src/auth/utils.ts index 910c5cee949..b455780f45d 100644 --- a/packages/core/src/auth/utils.ts +++ b/packages/core/src/auth/utils.ts @@ -40,6 +40,7 @@ import { hasScopes, scopesSsoAccountAccess, isSsoConnection, + IamConnection, } from './connection' import { Commands, placeholder } from '../shared/vscode/commands2' import { Auth } from './auth' @@ -79,6 +80,18 @@ export async function promptForConnection(auth: Auth, type?: 'iam' | 'iam-only' return globals.awsContextCommands.onCommandEditCredentials() } + // If selected connection is SSO connection and has linked IAM profiles, show second quick pick with the linked IAM profiles + if (isSsoConnection(resp)) { + const linkedProfiles = await getLinkedIamProfiles(auth, resp) + + if (linkedProfiles.length > 0) { + const linkedResp = await showLinkedProfilePicker(linkedProfiles, resp) + if (linkedResp) { + return linkedResp + } + } + } + return resp } @@ -340,6 +353,36 @@ export const createDeleteConnectionButton: () => vscode.QuickInputButton = () => return { tooltip: deleteConnection, iconPath: getIcon('vscode-trash') } } +async function getLinkedIamProfiles(auth: Auth, ssoConnection: SsoConnection): Promise { + const allConnections = await auth.listAndTraverseConnections().promise() + + return allConnections.filter( + (conn) => isIamConnection(conn) && conn.id.startsWith(`sso:${ssoConnection.id}#`) + ) as IamConnection[] +} + +/** + * Shows a quick pick with linked IAM profiles for a selected SSO connection + */ +async function showLinkedProfilePicker( + linkedProfiles: IamConnection[], + ssoConnection: SsoConnection +): Promise { + const title = `Select an IAM Role for ${ssoConnection.label}` + + const items: DataQuickPickItem[] = linkedProfiles.map((profile) => ({ + label: codicon`${getIcon('vscode-key')} ${profile.label}`, + description: 'IAM Credential, sourced from IAM Identity Center', + data: profile, + })) + + return await showQuickPick(items, { + title, + placeholder: 'Select an IAM role', + buttons: [createRefreshButton(), createExitButton()], + }) +} + export function createConnectionPrompter(auth: Auth, type?: 'iam' | 'iam-only' | 'sso') { const addNewConnection = { label: codicon`${getIcon('vscode-plus')} Add New Connection`, @@ -433,14 +476,14 @@ export function createConnectionPrompter(auth: Auth, type?: 'iam' | 'iam-only' | for await (const conn of connections) { if (conn.label.includes('profile:') && !hasShownEdit) { hasShownEdit = true - yield [toPickerItem(conn), editCredentials] + yield [await toPickerItem(conn), editCredentials] } else { - yield [toPickerItem(conn)] + yield [await toPickerItem(conn)] } } } - function toPickerItem(conn: Connection): DataQuickPickItem { + async function toPickerItem(conn: Connection): Promise> { const state = auth.getConnectionState(conn) // Only allow SSO connections to be deleted const deleteButton: vscode.QuickInputButton[] = conn.type === 'sso' ? [createDeleteConnectionButton()] : [] @@ -448,7 +491,7 @@ export function createConnectionPrompter(auth: Auth, type?: 'iam' | 'iam-only' | return { data: conn, label: codicon`${getConnectionIcon(conn)} ${conn.label}`, - description: getConnectionDescription(conn), + description: await getConnectionDescription(conn), buttons: [...deleteButton], } } @@ -502,7 +545,7 @@ export function createConnectionPrompter(auth: Auth, type?: 'iam' | 'iam-only' | } } - function getConnectionDescription(conn: Connection) { + async function getConnectionDescription(conn: Connection) { if (conn.type === 'iam') { // TODO: implement a proper `getConnectionSource` method to discover where a connection came from const descSuffix = conn.id.startsWith('profile:') @@ -514,6 +557,14 @@ export function createConnectionPrompter(auth: Auth, type?: 'iam' | 'iam-only' | return `IAM Credential, ${descSuffix}` } + // If this is an SSO connection, check if it has linked IAM profiles + if (isSsoConnection(conn)) { + const linkedProfiles = await getLinkedIamProfiles(auth, conn) + if (linkedProfiles.length > 0) { + return `Has ${linkedProfiles.length} IAM role${linkedProfiles.length > 1 ? 's' : ''} (click to select)` + } + } + const toolAuths = getDependentAuths(conn) if (toolAuths.length === 0) { return undefined From efb2d498af6d414d2f375499de4358ad2750620c Mon Sep 17 00:00:00 2001 From: Reed Hamilton Date: Fri, 18 Jul 2025 12:01:22 -0700 Subject: [PATCH 29/69] test: make sso quickpick option display profiles --- .../core/src/test/credentials/auth.test.ts | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/packages/core/src/test/credentials/auth.test.ts b/packages/core/src/test/credentials/auth.test.ts index 022e2c5c6e7..a409f2ba2e2 100644 --- a/packages/core/src/test/credentials/auth.test.ts +++ b/packages/core/src/test/credentials/auth.test.ts @@ -570,6 +570,47 @@ describe('Auth', function () { assert.strictEqual((await promptForConnection(auth))?.id, conn.id) }) + it('shows a second quickPick for linked IAM profiles when selecting an SSO connection', async function () { + let quickPickCount = 0 + getTestWindow().onDidShowQuickPick(async (picker) => { + await picker.untilReady() + quickPickCount++ + + if (quickPickCount === 1) { + // First picker: select the SSO connection + const connItem = picker.findItemOrThrow(/IAM Identity Center/) + picker.acceptItem(connItem) + } else if (quickPickCount === 2) { + // Second picker: select the linked IAM profile + const linkedItem = picker.findItemOrThrow(/TestRole/) + picker.acceptItem(linkedItem) + } + }) + + const linkedSsoProfile = createSsoProfile({ scopes: scopesSsoAccountAccess }) + const conn = await auth.createConnection(linkedSsoProfile) + + // Mock the SSOClient to return account roles + auth.ssoClient.listAccounts.returns( + toCollection(async function* () { + yield [{ accountId: '123456789012' }] + }) + ) + auth.ssoClient.listAccountRoles.callsFake(() => + toCollection(async function* () { + yield [{ accountId: '123456789012', roleName: 'TestRole' }] + }) + ) + + // Should get a linked IAM profile back, not the SSO connection + const result = await promptForConnection(auth) + assert.ok(isIamConnection(result || undefined), 'Expected an IAM connection to be returned') + assert.ok( + result?.id.startsWith(`sso:${conn.id}#`), + 'Expected the IAM connection to be linked to the SSO connection' + ) + }) + it('refreshes when clicking the refresh button', async function () { getTestWindow().onDidShowQuickPick(async (picker) => { await picker.untilReady() From 9f082d9009b1aeb00a4d5e9181f43f964862ea63 Mon Sep 17 00:00:00 2001 From: Reed Hamilton Date: Fri, 18 Jul 2025 12:57:01 -0700 Subject: [PATCH 30/69] changelog update --- .../Feature-852d6637-ac3c-4b7f-b846-649d23da87e3.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 packages/toolkit/.changes/next-release/Feature-852d6637-ac3c-4b7f-b846-649d23da87e3.json diff --git a/packages/toolkit/.changes/next-release/Feature-852d6637-ac3c-4b7f-b846-649d23da87e3.json b/packages/toolkit/.changes/next-release/Feature-852d6637-ac3c-4b7f-b846-649d23da87e3.json new file mode 100644 index 00000000000..2d7652be636 --- /dev/null +++ b/packages/toolkit/.changes/next-release/Feature-852d6637-ac3c-4b7f-b846-649d23da87e3.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "Improved connection actions for SSO" +} From fb6b847f721bddb65886d7ca40f3cdb3005e2960 Mon Sep 17 00:00:00 2001 From: abhraina-aws Date: Fri, 18 Jul 2025 13:21:03 -0700 Subject: [PATCH 31/69] fix(amazonq): match rotating logger level --- .../amazonq/src/lsp/rotatingLogChannel.ts | 3 +- .../src/test/rotatingLogChannel.test.ts | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/amazonq/src/lsp/rotatingLogChannel.ts b/packages/amazonq/src/lsp/rotatingLogChannel.ts index a9ee36ed30c..b8e3df276f9 100644 --- a/packages/amazonq/src/lsp/rotatingLogChannel.ts +++ b/packages/amazonq/src/lsp/rotatingLogChannel.ts @@ -12,7 +12,6 @@ export class RotatingLogChannel implements vscode.LogOutputChannel { private fileStream: fs.WriteStream | undefined private originalChannel: vscode.LogOutputChannel private logger = getLogger('amazonqLsp') - private _logLevel: vscode.LogLevel = vscode.LogLevel.Info private currentFileSize = 0 // eslint-disable-next-line @typescript-eslint/naming-convention private readonly MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB @@ -133,7 +132,7 @@ export class RotatingLogChannel implements vscode.LogOutputChannel { } get logLevel(): vscode.LogLevel { - return this._logLevel + return this.originalChannel.logLevel } get onDidChangeLogLevel(): vscode.Event { diff --git a/packages/amazonq/src/test/rotatingLogChannel.test.ts b/packages/amazonq/src/test/rotatingLogChannel.test.ts index ec963ebac9f..87c4c109603 100644 --- a/packages/amazonq/src/test/rotatingLogChannel.test.ts +++ b/packages/amazonq/src/test/rotatingLogChannel.test.ts @@ -161,4 +161,32 @@ describe('RotatingLogChannel', () => { assert.ok(content.includes('[WARN]'), 'Should include WARN level') assert.ok(content.includes('[ERROR]'), 'Should include ERROR level') }) + + it('delegates log level to the original channel', () => { + // Set up a mock output channel with a specific log level + const mockChannel = { + ...mockOutputChannel, + logLevel: vscode.LogLevel.Trace, + } + + // Create a new log channel with the mock + const testLogChannel = new RotatingLogChannel('test-delegate', mockExtensionContext, mockChannel) + + // Verify that the log level is delegated correctly + assert.strictEqual( + testLogChannel.logLevel, + vscode.LogLevel.Trace, + 'Should delegate log level to original channel' + ) + + // Change the mock's log level + mockChannel.logLevel = vscode.LogLevel.Debug + + // Verify that the change is reflected + assert.strictEqual( + testLogChannel.logLevel, + vscode.LogLevel.Debug, + 'Should reflect changes to original channel log level' + ) + }) }) From e62889f98f5cd43ccd58b0d94a8a0691a947139e Mon Sep 17 00:00:00 2001 From: mkovelam Date: Fri, 18 Jul 2025 15:59:56 -0700 Subject: [PATCH 32/69] feat(amazonq): enabling code review tool --- packages/amazonq/src/lsp/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index f577966a342..dce60a8a832 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -186,7 +186,7 @@ export async function startLanguageServer( reroute: true, modelSelection: true, workspaceFilePath: vscode.workspace.workspaceFile?.fsPath, - codeReviewInChat: false, + codeReviewInChat: true, }, window: { notifications: true, From 4fd2d45bbdc69fb2c907e6ddd26d50d512d98675 Mon Sep 17 00:00:00 2001 From: Na Yue Date: Fri, 18 Jul 2025 16:21:03 -0700 Subject: [PATCH 33/69] =?UTF-8?q?revert(amazonq):=20should=20pass=20nextTo?= =?UTF-8?q?ken=20to=20Flare=20for=20Edits=20on=20acc=E2=80=A6=20(#7710)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem This reverts commit 678851bbe9776228f55e0460e66a6167ac2a1685. ## Solution --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- scripts/package.ts | 67 ---------------------------------------------- 1 file changed, 67 deletions(-) diff --git a/scripts/package.ts b/scripts/package.ts index 264a8faabe6..203777e8131 100644 --- a/scripts/package.ts +++ b/scripts/package.ts @@ -20,7 +20,6 @@ import * as child_process from 'child_process' // eslint-disable-line no-restricted-imports import * as nodefs from 'fs' // eslint-disable-line no-restricted-imports import * as path from 'path' -import { platform } from 'os'; import { downloadLanguageServer } from './lspArtifact' function parseArgs() { @@ -107,67 +106,6 @@ function getVersionSuffix(feature: string, debug: boolean): string { return `${debugSuffix}${featureSuffix}${commitSuffix}` } -/** - * @returns true if curl is available - */ -function isCurlAvailable(): boolean { - try { - child_process.execFileSync('curl', ['--version']); - return true; - } catch { - return false; - } -} - -/** - * Small utility to download files. - */ -function downloadFiles(urls: string[], outputDir: string, outputFile: string): void { - if (platform() !== 'linux') { - return; - } - - if (!isCurlAvailable()) { - return; - } - - // Create output directory if it doesn't exist - if (!nodefs.existsSync(outputDir)) { - nodefs.mkdirSync(outputDir, { recursive: true }); - } - - urls.forEach(url => { - const filePath = path.join(outputDir, outputFile || ''); - - try { - child_process.execFileSync('curl', ['-o', filePath, url]); - } catch {} - }) -} - -/** - * Performs steps to ensure build stability. - * - * TODO: retrieve from authoritative system - */ -function preparePackager(): void { - const dir = process.cwd(); - const REPO_NAME = "aws/aws-toolkit-vscode" - const TAG_NAME = "stability" - - if (!dir.includes('amazonq')) { - return; - } - - if (process.env.STAGE !== 'prod') { - return; - } - - downloadFiles([ - `https://raw.githubusercontent.com/${REPO_NAME}/${TAG_NAME}/scripts/extensionNode.bk` - ], "src/", "extensionNode.ts") -} - async function main() { const args = parseArgs() // It is expected that this will package from a packages/{subproject} folder. @@ -189,11 +127,6 @@ async function main() { if (release && isBeta()) { throw new Error('Cannot package VSIX as both a release and a beta simultaneously') } - - if (release) { - preparePackager() - } - // Create backup file so we can restore the originals later. nodefs.copyFileSync(packageJsonFile, backupJsonFile) const packageJson = JSON.parse(nodefs.readFileSync(packageJsonFile, { encoding: 'utf-8' })) From ab7fb6ad170f2f7df71d2912dedfa22c3620553c Mon Sep 17 00:00:00 2001 From: Laxman Reddy <141967714+laileni-aws@users.noreply.github.com> Date: Fri, 18 Jul 2025 19:00:43 -0700 Subject: [PATCH 34/69] fix(amazonq): reverting for Amazon Q (#7714) This reverts until 1a5e3767a0a7ec8ca08f6855ade269d5847334e6. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Co-authored-by: Na Yue --- package-lock.json | 20 +- packages/amazonq/.changes/1.84.0.json | 18 -- ...-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json | 4 - ...-45aef014-07f3-4511-a9f6-d7233077784c.json | 4 + ...-91380b87-5955-4c15-b762-31e7f1c71575.json | 4 + ...-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json | 4 - ...-9e413673-5ef6-4920-97b1-e73635f3a0f5.json | 4 + packages/amazonq/CHANGELOG.md | 6 - packages/amazonq/package.json | 2 +- .../app/inline/EditRendering/imageRenderer.ts | 6 - packages/amazonq/src/app/inline/completion.ts | 15 +- .../src/app/inline/documentEventListener.ts | 19 -- .../src/app/inline/recommendationService.ts | 15 +- packages/amazonq/src/extension.ts | 25 +- packages/amazonq/src/lsp/chat/messages.ts | 17 +- packages/amazonq/src/lsp/client.ts | 115 ++------ packages/amazonq/src/lsp/config.ts | 10 +- .../amazonq/src/lsp/rotatingLogChannel.ts | 246 ---------------- .../src/test/rotatingLogChannel.test.ts | 192 ------------- .../apps/inline/recommendationService.test.ts | 2 - .../test/unit/amazonq/lsp/client.test.ts | 268 ------------------ .../test/unit/amazonq/lsp/config.test.ts | 148 ---------- .../EditRendering/imageRenderer.test.ts | 2 - .../securityIssueHoverProvider.test.ts | 36 +-- packages/core/package.json | 4 +- packages/core/src/auth/activation.ts | 2 +- .../codewhisperer/commands/basicCommands.ts | 6 - .../region/regionProfileManager.ts | 4 +- .../service/securityIssueHoverProvider.ts | 2 +- packages/core/src/shared/featureConfig.ts | 25 -- packages/core/src/shared/utilities/index.ts | 1 - .../src/shared/utilities/resourceCache.ts | 15 - .../core/src/test/auth/activation.test.ts | 146 ---------- packages/core/src/test/lambda/utils.test.ts | 25 -- 34 files changed, 85 insertions(+), 1327 deletions(-) delete mode 100644 packages/amazonq/.changes/1.84.0.json delete mode 100644 packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json create mode 100644 packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json create mode 100644 packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json delete mode 100644 packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json create mode 100644 packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json delete mode 100644 packages/amazonq/src/lsp/rotatingLogChannel.ts delete mode 100644 packages/amazonq/src/test/rotatingLogChannel.test.ts delete mode 100644 packages/amazonq/test/unit/amazonq/lsp/client.test.ts delete mode 100644 packages/core/src/test/auth/activation.test.ts diff --git a/package-lock.json b/package-lock.json index 6e8baa4a894..ed21305ffee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15044,13 +15044,13 @@ } }, "node_modules/@aws/language-server-runtimes": { - "version": "0.2.111", - "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes/-/language-server-runtimes-0.2.111.tgz", - "integrity": "sha512-eIHKzWkLTTb3qUCeT2nIrpP99dEv/OiUOcPB00MNCsOPWBBO/IoZhfGRNrE8+stgZMQkKLFH2ZYxn3ByB6OsCQ==", + "version": "0.2.102", + "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes/-/language-server-runtimes-0.2.102.tgz", + "integrity": "sha512-O68zmXClLP6mtKxh0fzGKYW3MwgFCTkAgL32WKzOWLwD6gMc5CaVRrNsZ2cabkAudf2laTeWeSDZJZsiQ0hCfA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws/language-server-runtimes-types": "^0.1.47", + "@aws/language-server-runtimes-types": "^0.1.43", "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.200.0", "@opentelemetry/core": "^2.0.0", @@ -15077,9 +15077,9 @@ } }, "node_modules/@aws/language-server-runtimes-types": { - "version": "0.1.47", - "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes-types/-/language-server-runtimes-types-0.1.47.tgz", - "integrity": "sha512-l5dOdx/MR3SO0HYXkSL9fcR05f4Aw7qRMuASMdWOK93LOSZeANPVOGIWblRnoJejfYiPXcufCFyjLnGpATExag==", + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes-types/-/language-server-runtimes-types-0.1.43.tgz", + "integrity": "sha512-qXaAGkiJ1hldF+Ynu6ZBXS18s47UOnbZEHxKiGRrBlBX2L75ih/4yasj8ITgshqS5Kx5JMntu+8vpc0CkGV6jA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -29954,7 +29954,7 @@ }, "packages/amazonq": { "name": "amazon-q-vscode", - "version": "1.85.0-SNAPSHOT", + "version": "1.84.0-SNAPSHOT", "license": "Apache-2.0", "dependencies": { "aws-core-vscode": "file:../core/" @@ -30063,8 +30063,8 @@ "@aws-sdk/types": "^3.13.1", "@aws/chat-client": "^0.1.4", "@aws/chat-client-ui-types": "^0.1.47", - "@aws/language-server-runtimes": "^0.2.111", - "@aws/language-server-runtimes-types": "^0.1.47", + "@aws/language-server-runtimes": "^0.2.102", + "@aws/language-server-runtimes-types": "^0.1.43", "@cspotcode/source-map-support": "^0.8.1", "@sinonjs/fake-timers": "^10.0.2", "@types/adm-zip": "^0.4.34", diff --git a/packages/amazonq/.changes/1.84.0.json b/packages/amazonq/.changes/1.84.0.json deleted file mode 100644 index e73a685e054..00000000000 --- a/packages/amazonq/.changes/1.84.0.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "date": "2025-07-17", - "version": "1.84.0", - "entries": [ - { - "type": "Bug Fix", - "description": "Slightly delay rendering inline completion when user is typing" - }, - { - "type": "Bug Fix", - "description": "Render first response before receiving all paginated inline completion results" - }, - { - "type": "Feature", - "description": "Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab." - } - ] -} \ No newline at end of file diff --git a/packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json b/packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json deleted file mode 100644 index 1a9e5c32e6d..00000000000 --- a/packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "Let Enter invoke auto completion more consistently" -} diff --git a/packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json b/packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json new file mode 100644 index 00000000000..4d45af73411 --- /dev/null +++ b/packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Slightly delay rendering inline completion when user is typing" +} diff --git a/packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json b/packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json new file mode 100644 index 00000000000..72293c3b97a --- /dev/null +++ b/packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Render first response before receiving all paginated inline completion results" +} diff --git a/packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json b/packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json deleted file mode 100644 index f2234549a0d..00000000000 --- a/packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "Use documentChangeEvent as auto trigger condition" -} diff --git a/packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json b/packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json new file mode 100644 index 00000000000..af699a24355 --- /dev/null +++ b/packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab." +} diff --git a/packages/amazonq/CHANGELOG.md b/packages/amazonq/CHANGELOG.md index ccf3fb8a215..980abde9d63 100644 --- a/packages/amazonq/CHANGELOG.md +++ b/packages/amazonq/CHANGELOG.md @@ -1,9 +1,3 @@ -## 1.84.0 2025-07-17 - -- **Bug Fix** Slightly delay rendering inline completion when user is typing -- **Bug Fix** Render first response before receiving all paginated inline completion results -- **Feature** Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab. - ## 1.83.0 2025-07-09 - **Feature** Amazon Q /test, /doc, and /dev capabilities integrated into Agentic coding. diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index fd83354aca8..25ab19b6ffb 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -2,7 +2,7 @@ "name": "amazon-q-vscode", "displayName": "Amazon Q", "description": "The most capable generative AI-powered assistant for building, operating, and transforming software, with advanced capabilities for managing data and AI", - "version": "1.85.0-SNAPSHOT", + "version": "1.84.0-SNAPSHOT", "extensionKind": [ "workspace" ], diff --git a/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts b/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts index 195879ff779..9af3878ef82 100644 --- a/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts +++ b/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts @@ -29,12 +29,6 @@ export async function showEdits( const { svgImage, startLine, newCode, origionalCodeHighlightRange } = await svgGenerationService.generateDiffSvg(currentFile, item.insertText as string) - // TODO: To investigate why it fails and patch [generateDiffSvg] - if (newCode.length === 0) { - getLogger('nextEditPrediction').warn('not able to apply provided edit suggestion, skip rendering') - return - } - if (svgImage) { // display the SVG image await displaySvgDecoration( diff --git a/packages/amazonq/src/app/inline/completion.ts b/packages/amazonq/src/app/inline/completion.ts index 9020deac824..360be53e67a 100644 --- a/packages/amazonq/src/app/inline/completion.ts +++ b/packages/amazonq/src/app/inline/completion.ts @@ -241,12 +241,6 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem return [] } - const isAutoTrigger = context.triggerKind === InlineCompletionTriggerKind.Automatic - if (isAutoTrigger && !CodeSuggestionsState.instance.isSuggestionsEnabled()) { - // return early when suggestions are disabled with auto trigger - return [] - } - // yield event loop to let the document listen catch updates await sleep(1) // prevent user deletion invoking auto trigger @@ -260,6 +254,12 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem try { const t0 = performance.now() vsCodeState.isRecommendationsActive = true + const isAutoTrigger = context.triggerKind === InlineCompletionTriggerKind.Automatic + if (isAutoTrigger && !CodeSuggestionsState.instance.isSuggestionsEnabled()) { + // return early when suggestions are disabled with auto trigger + return [] + } + // handling previous session const prevSession = this.sessionManager.getActiveSession() const prevSessionId = prevSession?.sessionId @@ -335,8 +335,7 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem context, token, isAutoTrigger, - getAllRecommendationsOptions, - this.documentEventListener.getLastDocumentChangeEvent(document.uri.fsPath)?.event + getAllRecommendationsOptions ) // get active item from session for displaying const items = this.sessionManager.getActiveRecommendation() diff --git a/packages/amazonq/src/app/inline/documentEventListener.ts b/packages/amazonq/src/app/inline/documentEventListener.ts index 36f65dc7331..4e60b595ce2 100644 --- a/packages/amazonq/src/app/inline/documentEventListener.ts +++ b/packages/amazonq/src/app/inline/documentEventListener.ts @@ -21,11 +21,6 @@ export class DocumentEventListener { this.lastDocumentChangeEventMap.clear() } this.lastDocumentChangeEventMap.set(e.document.uri.fsPath, { event: e, timestamp: performance.now() }) - // The VS Code provideInlineCompletionCallback may not trigger when Enter is pressed, especially in Python files - // manually make this trigger. In case of duplicate, the provideInlineCompletionCallback is already debounced - if (this.isEnter(e) && vscode.window.activeTextEditor) { - void vscode.commands.executeCommand('editor.action.inlineSuggest.trigger') - } } }) } @@ -52,18 +47,4 @@ export class DocumentEventListener { this.documentChangeListener.dispose() } } - - private isEnter(e: vscode.TextDocumentChangeEvent): boolean { - if (e.contentChanges.length !== 1) { - return false - } - const str = e.contentChanges[0].text - if (str.length === 0) { - return false - } - return ( - (str.startsWith('\r\n') && str.substring(2).trim() === '') || - (str[0] === '\n' && str.substring(1).trim() === '') - ) - } } diff --git a/packages/amazonq/src/app/inline/recommendationService.ts b/packages/amazonq/src/app/inline/recommendationService.ts index 1329c68a51c..ddde310999f 100644 --- a/packages/amazonq/src/app/inline/recommendationService.ts +++ b/packages/amazonq/src/app/inline/recommendationService.ts @@ -2,12 +2,10 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ -import * as vscode from 'vscode' import { InlineCompletionListWithReferences, InlineCompletionWithReferencesParams, inlineCompletionWithReferencesRequestType, - TextDocumentContentChangeEvent, } from '@aws/language-server-runtimes/protocol' import { CancellationToken, InlineCompletionContext, Position, TextDocument } from 'vscode' import { LanguageClient } from 'vscode-languageclient' @@ -42,20 +40,10 @@ export class RecommendationService { context: InlineCompletionContext, token: CancellationToken, isAutoTrigger: boolean, - options: GetAllRecommendationsOptions = { emitTelemetry: true, showUi: true }, - documentChangeEvent?: vscode.TextDocumentChangeEvent + options: GetAllRecommendationsOptions = { emitTelemetry: true, showUi: true } ) { // Record that a regular request is being made this.cursorUpdateRecorder?.recordCompletionRequest() - const documentChangeParams = documentChangeEvent - ? { - textDocument: { - uri: document.uri.toString(), - version: document.version, - }, - contentChanges: documentChangeEvent.contentChanges.map((x) => x as TextDocumentContentChangeEvent), - } - : undefined let request: InlineCompletionWithReferencesParams = { textDocument: { @@ -63,7 +51,6 @@ export class RecommendationService { }, position, context, - documentChangeParams: documentChangeParams, } if (options.editsStreakToken) { request = { ...request, partialResultToken: options.editsStreakToken } diff --git a/packages/amazonq/src/extension.ts b/packages/amazonq/src/extension.ts index 1e26724ff61..9ca13136eab 100644 --- a/packages/amazonq/src/extension.ts +++ b/packages/amazonq/src/extension.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth' +import { Auth, AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth' import { activate as activateCodeWhisperer, shutdown as shutdownCodeWhisperer } from 'aws-core-vscode/codewhisperer' import { makeEndpointsProvider, registerGenericCommands } from 'aws-core-vscode' import { CommonAuthWebview } from 'aws-core-vscode/login' @@ -44,8 +44,8 @@ import * as vscode from 'vscode' import { registerCommands } from './commands' import { focusAmazonQPanel } from 'aws-core-vscode/codewhispererChat' import { activate as activateAmazonqLsp } from './lsp/activation' +import { activate as activateInlineCompletion } from './app/inline/activation' import { hasGlibcPatch } from './lsp/client' -import { RotatingLogChannel } from './lsp/rotatingLogChannel' export const amazonQContextPrefix = 'amazonq' @@ -104,12 +104,7 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is globals.manifestPaths.endpoints = context.asAbsolutePath(join('resources', 'endpoints.json')) globals.regionProvider = RegionProvider.fromEndpointsProvider(makeEndpointsProvider()) - // Create rotating log channel for all Amazon Q logs - const qLogChannel = new RotatingLogChannel( - 'Amazon Q Logs', - context, - vscode.window.createOutputChannel('Amazon Q Logs', { log: true }) - ) + const qLogChannel = vscode.window.createOutputChannel('Amazon Q Logs', { log: true }) await activateLogger(context, amazonQContextPrefix, qLogChannel) globals.logOutputChannel = qLogChannel globals.loginManager = new LoginManager(globals.awsContext, new CredentialsStore()) @@ -118,8 +113,6 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is getLogger().error('fs.init: invalid env vars found: %O', homeDirLogs) } - getLogger().info('Rotating logger has been setup') - await activateTelemetry(context, globals.awsContext, Settings.instance, 'Amazon Q For VS Code') await initializeAuth(globals.loginManager) @@ -133,11 +126,17 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is // This contains every lsp agnostic things (auth, security scan, code scan) await activateCodeWhisperer(extContext as ExtContext) - - if (!isAmazonLinux2() || hasGlibcPatch()) { - // Activate Amazon Q LSP for everyone unless they're using AL2 without the glibc patch + if ( + (Experiments.instance.get('amazonqLSP', true) || Auth.instance.isInternalAmazonUser()) && + (!isAmazonLinux2() || hasGlibcPatch()) + ) { + // start the Amazon Q LSP for internal users first + // for AL2, start LSP if glibc patch is found await activateAmazonqLsp(context) } + if (!Experiments.instance.get('amazonqLSPInline', true)) { + await activateInlineCompletion() + } // Generic extension commands registerGenericCommands(context, amazonQContextPrefix) diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index f869bbe0da3..9841c7edee9 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -95,7 +95,6 @@ import { decryptResponse, encryptRequest } from '../encryption' import { getCursorState } from '../utils' import { focusAmazonQPanel } from './commands' import { ChatMessage } from '@aws/language-server-runtimes/server-interface' -import { CommentUtils } from 'aws-core-vscode/utils' export function registerActiveEditorChangeListener(languageClient: LanguageClient) { let debounceTimer: NodeJS.Timeout | undefined @@ -702,7 +701,7 @@ async function handleCompleteResult( ) { const decryptedMessage = await decryptResponse(result, encryptionKey) - await handleSecurityFindings(decryptedMessage, languageClient) + handleSecurityFindings(decryptedMessage, languageClient) void provider.webview?.postMessage({ command: chatRequestType.method, @@ -717,10 +716,10 @@ async function handleCompleteResult( disposable.dispose() } -async function handleSecurityFindings( +function handleSecurityFindings( decryptedMessage: { additionalMessages?: ChatMessage[] }, languageClient: LanguageClient -): Promise { +): void { if (decryptedMessage.additionalMessages === undefined || decryptedMessage.additionalMessages.length === 0) { return } @@ -731,18 +730,10 @@ async function handleSecurityFindings( try { const aggregatedCodeScanIssues: AggregatedCodeScanIssue[] = JSON.parse(message.body) for (const aggregatedCodeScanIssue of aggregatedCodeScanIssues) { - const document = await vscode.workspace.openTextDocument(aggregatedCodeScanIssue.filePath) for (const issue of aggregatedCodeScanIssue.issues) { - const isIssueTitleIgnored = CodeWhispererSettings.instance + issue.visible = !CodeWhispererSettings.instance .getIgnoredSecurityIssues() .includes(issue.title) - const isSingleIssueIgnored = CommentUtils.detectCommentAboveLine( - document, - issue.startLine, - CodeWhispererConstants.amazonqIgnoreNextLine - ) - - issue.visible = !isIssueTitleIgnored && !isSingleIssueIgnored } } initSecurityScanRender(aggregatedCodeScanIssues, undefined, CodeAnalysisScope.PROJECT) diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index e94842123ac..e6ef1e5dd9c 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -8,7 +8,6 @@ import * as nls from 'vscode-nls' import { LanguageClient, LanguageClientOptions, RequestType, State } from 'vscode-languageclient' import { InlineCompletionManager } from '../app/inline/completion' import { AmazonQLspAuth, encryptionKey, notificationTypes } from './auth' -import { RotatingLogChannel } from './rotatingLogChannel' import { CreateFilesParams, DeleteFilesParams, @@ -95,23 +94,6 @@ export async function startLanguageServer( const clientId = 'amazonq' const traceServerEnabled = Settings.instance.isSet(`${clientId}.trace.server`) - - // Create custom output channel that writes to disk but sends UI output to the appropriate channel - const lspLogChannel = new RotatingLogChannel( - traceServerEnabled ? 'Amazon Q Language Server' : 'Amazon Q Logs', - extensionContext, - traceServerEnabled - ? vscode.window.createOutputChannel('Amazon Q Language Server', { log: true }) - : globals.logOutputChannel - ) - - // Add cleanup for our file output channel - toDispose.push({ - dispose: () => { - lspLogChannel.dispose() - }, - }) - let executable: string[] = [] // apply the GLIBC 2.28 path to node js runtime binary if (isSageMaker()) { @@ -186,7 +168,6 @@ export async function startLanguageServer( reroute: true, modelSelection: true, workspaceFilePath: vscode.workspace.workspaceFile?.fsPath, - qCodeReviewInChat: true, }, window: { notifications: true, @@ -209,9 +190,15 @@ export async function startLanguageServer( }, }, /** - * Using our RotatingLogger for all logs + * When the trace server is enabled it outputs a ton of log messages so: + * When trace server is enabled, logs go to a seperate "Amazon Q Language Server" output. + * Otherwise, logs go to the regular "Amazon Q Logs" channel. */ - outputChannel: lspLogChannel, + ...(traceServerEnabled + ? {} + : { + outputChannel: globals.logOutputChannel, + }), } const client = new LanguageClient( @@ -264,59 +251,6 @@ async function initializeAuth(client: LanguageClient): Promise { return auth } -// jscpd:ignore-start -async function initializeLanguageServerConfiguration(client: LanguageClient, context: string = 'startup') { - const logger = getLogger('amazonqLsp') - - if (AuthUtil.instance.isConnectionValid()) { - logger.info(`[${context}] Initializing language server configuration`) - // jscpd:ignore-end - - try { - // Send profile configuration - logger.debug(`[${context}] Sending profile configuration to language server`) - await sendProfileToLsp(client) - logger.debug(`[${context}] Profile configuration sent successfully`) - - // Send customization configuration - logger.debug(`[${context}] Sending customization configuration to language server`) - await pushConfigUpdate(client, { - type: 'customization', - customization: getSelectedCustomization(), - }) - logger.debug(`[${context}] Customization configuration sent successfully`) - - logger.info(`[${context}] Language server configuration completed successfully`) - } catch (error) { - logger.error(`[${context}] Failed to initialize language server configuration: ${error}`) - throw error - } - } else { - logger.warn( - `[${context}] Connection invalid, skipping language server configuration - this will cause authentication failures` - ) - const activeConnection = AuthUtil.instance.auth.activeConnection - const connectionState = activeConnection - ? AuthUtil.instance.auth.getConnectionState(activeConnection) - : 'no-connection' - logger.warn(`[${context}] Connection state: ${connectionState}`) - } -} - -async function sendProfileToLsp(client: LanguageClient) { - const logger = getLogger('amazonqLsp') - const profileArn = AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn - - logger.debug(`Sending profile to LSP: ${profileArn || 'undefined'}`) - - await pushConfigUpdate(client, { - type: 'profile', - profileArn: profileArn, - }) - - logger.debug(`Profile sent to LSP successfully`) -} - async function onLanguageServerReady( extensionContext: vscode.ExtensionContext, auth: AmazonQLspAuth, @@ -348,7 +282,14 @@ async function onLanguageServerReady( // We manually push the cached values the first time since event handlers, which should push, may not have been setup yet. // Execution order is weird and should be fixed in the flare implementation. // TODO: Revisit if we need this if we setup the event handlers properly - await initializeLanguageServerConfiguration(client, 'startup') + if (AuthUtil.instance.isConnectionValid()) { + await sendProfileToLsp(client) + + await pushConfigUpdate(client, { + type: 'customization', + customization: getSelectedCustomization(), + }) + } toDispose.push( inlineManager, @@ -450,6 +391,13 @@ async function onLanguageServerReady( // Set this inside onReady so that it only triggers on subsequent language server starts (not the first) onServerRestartHandler(client, auth) ) + + async function sendProfileToLsp(client: LanguageClient) { + await pushConfigUpdate(client, { + type: 'profile', + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + } } /** @@ -469,21 +417,8 @@ function onServerRestartHandler(client: LanguageClient, auth: AmazonQLspAuth) { // TODO: Port this metric override to common definitions telemetry.languageServer_crash.emit({ id: 'AmazonQ' }) - const logger = getLogger('amazonqLsp') - logger.info('[crash-recovery] Language server crash detected, reinitializing authentication') - - try { - // Send bearer token - logger.debug('[crash-recovery] Refreshing connection and sending bearer token') - await auth.refreshConnection(true) - logger.debug('[crash-recovery] Bearer token sent successfully') - - // Send profile and customization configuration - await initializeLanguageServerConfiguration(client, 'crash-recovery') - logger.info('[crash-recovery] Authentication reinitialized successfully') - } catch (error) { - logger.error(`[crash-recovery] Failed to reinitialize after crash: ${error}`) - } + // Need to set the auth token in the again + await auth.refreshConnection(true) }) } diff --git a/packages/amazonq/src/lsp/config.ts b/packages/amazonq/src/lsp/config.ts index 6b88eb98d21..66edc9ff6f1 100644 --- a/packages/amazonq/src/lsp/config.ts +++ b/packages/amazonq/src/lsp/config.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ import * as vscode from 'vscode' -import { DevSettings, getServiceEnvVarConfig, BaseLspInstaller, getLogger } from 'aws-core-vscode/shared' +import { DevSettings, getServiceEnvVarConfig, BaseLspInstaller } from 'aws-core-vscode/shared' import { LanguageClient } from 'vscode-languageclient' import { DidChangeConfigurationNotification, @@ -68,31 +68,23 @@ export function toAmazonQLSPLogLevel(logLevel: vscode.LogLevel): LspLogLevel { * push the given config. */ export async function pushConfigUpdate(client: LanguageClient, config: QConfigs) { - const logger = getLogger('amazonqLsp') - switch (config.type) { case 'profile': - logger.debug(`Pushing profile configuration: ${config.profileArn || 'undefined'}`) await client.sendRequest(updateConfigurationRequestType.method, { section: 'aws.q', settings: { profileArn: config.profileArn }, }) - logger.debug(`Profile configuration pushed successfully`) break case 'customization': - logger.debug(`Pushing customization configuration: ${config.customization || 'undefined'}`) client.sendNotification(DidChangeConfigurationNotification.type.method, { section: 'aws.q', settings: { customization: config.customization }, }) - logger.debug(`Customization configuration pushed successfully`) break case 'logLevel': - logger.debug(`Pushing log level configuration`) client.sendNotification(DidChangeConfigurationNotification.type.method, { section: 'aws.logLevel', }) - logger.debug(`Log level configuration pushed successfully`) break } } diff --git a/packages/amazonq/src/lsp/rotatingLogChannel.ts b/packages/amazonq/src/lsp/rotatingLogChannel.ts deleted file mode 100644 index b8e3df276f9..00000000000 --- a/packages/amazonq/src/lsp/rotatingLogChannel.ts +++ /dev/null @@ -1,246 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import * as path from 'path' -import * as fs from 'fs' // eslint-disable-line no-restricted-imports -import { getLogger } from 'aws-core-vscode/shared' - -export class RotatingLogChannel implements vscode.LogOutputChannel { - private fileStream: fs.WriteStream | undefined - private originalChannel: vscode.LogOutputChannel - private logger = getLogger('amazonqLsp') - private currentFileSize = 0 - // eslint-disable-next-line @typescript-eslint/naming-convention - private readonly MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB - // eslint-disable-next-line @typescript-eslint/naming-convention - private readonly MAX_LOG_FILES = 4 - private static currentLogPath: string | undefined - - private static generateNewLogPath(logDir: string): string { - const timestamp = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '-').replace('Z', '') - return path.join(logDir, `amazonq-lsp-${timestamp}.log`) - } - - constructor( - public readonly name: string, - private readonly extensionContext: vscode.ExtensionContext, - outputChannel: vscode.LogOutputChannel - ) { - this.originalChannel = outputChannel - this.initFileStream() - } - - private async cleanupOldLogs(): Promise { - try { - const logDir = this.extensionContext.storageUri?.fsPath - if (!logDir) { - return - } - - // Get all log files - const files = await fs.promises.readdir(logDir) - const logFiles = files - .filter((f) => f.startsWith('amazonq-lsp-') && f.endsWith('.log')) - .map((f) => ({ - name: f, - path: path.join(logDir, f), - time: fs.statSync(path.join(logDir, f)).mtime.getTime(), - })) - .sort((a, b) => b.time - a.time) // Sort newest to oldest - - // Remove all but the most recent MAX_LOG_FILES files - for (const file of logFiles.slice(this.MAX_LOG_FILES - 1)) { - try { - await fs.promises.unlink(file.path) - this.logger.debug(`Removed old log file: ${file.path}`) - } catch (err) { - this.logger.error(`Failed to remove old log file ${file.path}: ${err}`) - } - } - } catch (err) { - this.logger.error(`Failed to cleanup old logs: ${err}`) - } - } - - private getLogFilePath(): string { - // If we already have a path, reuse it - if (RotatingLogChannel.currentLogPath) { - return RotatingLogChannel.currentLogPath - } - - const logDir = this.extensionContext.storageUri?.fsPath - if (!logDir) { - throw new Error('No storage URI available') - } - - // Generate initial path - RotatingLogChannel.currentLogPath = RotatingLogChannel.generateNewLogPath(logDir) - return RotatingLogChannel.currentLogPath - } - - private async rotateLog(): Promise { - try { - // Close current stream - if (this.fileStream) { - this.fileStream.end() - } - - const logDir = this.extensionContext.storageUri?.fsPath - if (!logDir) { - throw new Error('No storage URI available') - } - - // Generate new path directly - RotatingLogChannel.currentLogPath = RotatingLogChannel.generateNewLogPath(logDir) - - // Create new log file with new path - this.fileStream = fs.createWriteStream(RotatingLogChannel.currentLogPath, { flags: 'a' }) - this.currentFileSize = 0 - - // Clean up old files - await this.cleanupOldLogs() - - this.logger.info(`Created new log file: ${RotatingLogChannel.currentLogPath}`) - } catch (err) { - this.logger.error(`Failed to rotate log file: ${err}`) - } - } - - private initFileStream() { - try { - const logDir = this.extensionContext.storageUri - if (!logDir) { - this.logger.error('Failed to get storage URI for logs') - return - } - - // Ensure directory exists - if (!fs.existsSync(logDir.fsPath)) { - fs.mkdirSync(logDir.fsPath, { recursive: true }) - } - - const logPath = this.getLogFilePath() - this.fileStream = fs.createWriteStream(logPath, { flags: 'a' }) - this.currentFileSize = 0 - this.logger.info(`Logging to file: ${logPath}`) - } catch (err) { - this.logger.error(`Failed to create log file: ${err}`) - } - } - - get logLevel(): vscode.LogLevel { - return this.originalChannel.logLevel - } - - get onDidChangeLogLevel(): vscode.Event { - return this.originalChannel.onDidChangeLogLevel - } - - trace(message: string, ...args: any[]): void { - this.originalChannel.trace(message, ...args) - this.writeToFile(`[TRACE] ${message}`) - } - - debug(message: string, ...args: any[]): void { - this.originalChannel.debug(message, ...args) - this.writeToFile(`[DEBUG] ${message}`) - } - - info(message: string, ...args: any[]): void { - this.originalChannel.info(message, ...args) - this.writeToFile(`[INFO] ${message}`) - } - - warn(message: string, ...args: any[]): void { - this.originalChannel.warn(message, ...args) - this.writeToFile(`[WARN] ${message}`) - } - - error(message: string | Error, ...args: any[]): void { - this.originalChannel.error(message, ...args) - this.writeToFile(`[ERROR] ${message instanceof Error ? message.stack || message.message : message}`) - } - - append(value: string): void { - this.originalChannel.append(value) - this.writeToFile(value) - } - - appendLine(value: string): void { - this.originalChannel.appendLine(value) - this.writeToFile(value + '\n') - } - - replace(value: string): void { - this.originalChannel.replace(value) - this.writeToFile(`[REPLACE] ${value}`) - } - - clear(): void { - this.originalChannel.clear() - } - - show(preserveFocus?: boolean): void - show(column?: vscode.ViewColumn, preserveFocus?: boolean): void - show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void { - if (typeof columnOrPreserveFocus === 'boolean') { - this.originalChannel.show(columnOrPreserveFocus) - } else { - this.originalChannel.show(columnOrPreserveFocus, preserveFocus) - } - } - - hide(): void { - this.originalChannel.hide() - } - - dispose(): void { - // First dispose the original channel - this.originalChannel.dispose() - - // Close our file stream if it exists - if (this.fileStream) { - this.fileStream.end() - } - - // Clean up all log files - const logDir = this.extensionContext.storageUri?.fsPath - if (logDir) { - try { - const files = fs.readdirSync(logDir) - for (const file of files) { - if (file.startsWith('amazonq-lsp-') && file.endsWith('.log')) { - fs.unlinkSync(path.join(logDir, file)) - } - } - this.logger.info('Cleaned up all log files during disposal') - } catch (err) { - this.logger.error(`Failed to cleanup log files during disposal: ${err}`) - } - } - } - - private writeToFile(content: string): void { - if (this.fileStream) { - try { - const timestamp = new Date().toISOString() - const logLine = `${timestamp} ${content}\n` - const size = Buffer.byteLength(logLine) - - // If this write would exceed max file size, rotate first - if (this.currentFileSize + size > this.MAX_FILE_SIZE) { - void this.rotateLog() - } - - this.fileStream.write(logLine) - this.currentFileSize += size - } catch (err) { - this.logger.error(`Failed to write to log file: ${err}`) - void this.rotateLog() - } - } - } -} diff --git a/packages/amazonq/src/test/rotatingLogChannel.test.ts b/packages/amazonq/src/test/rotatingLogChannel.test.ts deleted file mode 100644 index 87c4c109603..00000000000 --- a/packages/amazonq/src/test/rotatingLogChannel.test.ts +++ /dev/null @@ -1,192 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -// eslint-disable-next-line no-restricted-imports -import * as fs from 'fs' -import * as path from 'path' -import * as assert from 'assert' -import { RotatingLogChannel } from '../lsp/rotatingLogChannel' - -describe('RotatingLogChannel', () => { - let testDir: string - let mockExtensionContext: vscode.ExtensionContext - let mockOutputChannel: vscode.LogOutputChannel - let logChannel: RotatingLogChannel - - beforeEach(() => { - // Create a temp test directory - testDir = fs.mkdtempSync('amazonq-test-logs-') - - // Mock extension context - mockExtensionContext = { - storageUri: { fsPath: testDir } as vscode.Uri, - } as vscode.ExtensionContext - - // Mock output channel - mockOutputChannel = { - name: 'Test Output Channel', - append: () => {}, - appendLine: () => {}, - replace: () => {}, - clear: () => {}, - show: () => {}, - hide: () => {}, - dispose: () => {}, - trace: () => {}, - debug: () => {}, - info: () => {}, - warn: () => {}, - error: () => {}, - logLevel: vscode.LogLevel.Info, - onDidChangeLogLevel: new vscode.EventEmitter().event, - } - - // Create log channel instance - logChannel = new RotatingLogChannel('test', mockExtensionContext, mockOutputChannel) - }) - - afterEach(() => { - // Cleanup test directory - if (fs.existsSync(testDir)) { - fs.rmSync(testDir, { recursive: true, force: true }) - } - }) - - it('creates log file on initialization', () => { - const files = fs.readdirSync(testDir) - assert.strictEqual(files.length, 1) - assert.ok(files[0].startsWith('amazonq-lsp-')) - assert.ok(files[0].endsWith('.log')) - }) - - it('writes logs to file', async () => { - const testMessage = 'test log message' - logChannel.info(testMessage) - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') - assert.ok(content.includes(testMessage)) - }) - - it('rotates files when size limit is reached', async () => { - // Write enough data to trigger rotation - const largeMessage = 'x'.repeat(1024 * 1024) // 1MB - for (let i = 0; i < 6; i++) { - // Should create at least 2 files - logChannel.info(largeMessage) - } - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - assert.ok(files.length > 1, 'Should have created multiple log files') - assert.ok(files.length <= 4, 'Should not exceed max file limit') - }) - - it('keeps only the specified number of files', async () => { - // Write enough data to create more than MAX_LOG_FILES - const largeMessage = 'x'.repeat(1024 * 1024) // 1MB - for (let i = 0; i < 20; i++) { - // Should trigger multiple rotations - logChannel.info(largeMessage) - } - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - assert.strictEqual(files.length, 4, 'Should keep exactly 4 files') - }) - - it('cleans up all files on dispose', async () => { - // Write some logs - logChannel.info('test message') - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - // Verify files exist - assert.ok(fs.readdirSync(testDir).length > 0) - - // Dispose - logChannel.dispose() - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - // Verify files are cleaned up - const remainingFiles = fs.readdirSync(testDir).filter((f) => f.startsWith('amazonq-lsp-') && f.endsWith('.log')) - assert.strictEqual(remainingFiles.length, 0, 'Should have no log files after disposal') - }) - - it('includes timestamps in log messages', async () => { - const testMessage = 'test message' - logChannel.info(testMessage) - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') - - // ISO date format regex - const timestampRegex = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/ - assert.ok(timestampRegex.test(content), 'Log entry should include ISO timestamp') - }) - - it('handles different log levels correctly', async () => { - const testMessage = 'test message' - logChannel.trace(testMessage) - logChannel.debug(testMessage) - logChannel.info(testMessage) - logChannel.warn(testMessage) - logChannel.error(testMessage) - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') - - assert.ok(content.includes('[TRACE]'), 'Should include TRACE level') - assert.ok(content.includes('[DEBUG]'), 'Should include DEBUG level') - assert.ok(content.includes('[INFO]'), 'Should include INFO level') - assert.ok(content.includes('[WARN]'), 'Should include WARN level') - assert.ok(content.includes('[ERROR]'), 'Should include ERROR level') - }) - - it('delegates log level to the original channel', () => { - // Set up a mock output channel with a specific log level - const mockChannel = { - ...mockOutputChannel, - logLevel: vscode.LogLevel.Trace, - } - - // Create a new log channel with the mock - const testLogChannel = new RotatingLogChannel('test-delegate', mockExtensionContext, mockChannel) - - // Verify that the log level is delegated correctly - assert.strictEqual( - testLogChannel.logLevel, - vscode.LogLevel.Trace, - 'Should delegate log level to original channel' - ) - - // Change the mock's log level - mockChannel.logLevel = vscode.LogLevel.Debug - - // Verify that the change is reflected - assert.strictEqual( - testLogChannel.logLevel, - vscode.LogLevel.Debug, - 'Should reflect changes to original channel log level' - ) - }) -}) diff --git a/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts b/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts index 54eea8347c5..744fcc63c53 100644 --- a/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts +++ b/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts @@ -146,7 +146,6 @@ describe('RecommendationService', () => { }, position: mockPosition, context: mockContext, - documentChangeParams: undefined, }) // Verify session management @@ -188,7 +187,6 @@ describe('RecommendationService', () => { }, position: mockPosition, context: mockContext, - documentChangeParams: undefined, } const secondRequestArgs = sendRequestStub.secondCall.args[1] assert.deepStrictEqual(firstRequestArgs, expectedRequestArgs) diff --git a/packages/amazonq/test/unit/amazonq/lsp/client.test.ts b/packages/amazonq/test/unit/amazonq/lsp/client.test.ts deleted file mode 100644 index 7c99c47e0ea..00000000000 --- a/packages/amazonq/test/unit/amazonq/lsp/client.test.ts +++ /dev/null @@ -1,268 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import assert from 'assert' -import sinon from 'sinon' -import { LanguageClient } from 'vscode-languageclient' -import { AuthUtil } from 'aws-core-vscode/codewhisperer' -import { AmazonQLspAuth } from '../../../../src/lsp/auth' - -// These tests verify the behavior of the authentication functions -// Since the actual functions are module-level and use real dependencies, -// we test the expected behavior through mock implementations - -describe('Language Server Client Authentication', function () { - let sandbox: sinon.SinonSandbox - let mockClient: any - let mockAuth: any - let authUtilStub: sinon.SinonStub - let loggerStub: any - let getLoggerStub: sinon.SinonStub - let pushConfigUpdateStub: sinon.SinonStub - - beforeEach(() => { - sandbox = sinon.createSandbox() - - // Mock LanguageClient - mockClient = { - sendRequest: sandbox.stub().resolves(), - sendNotification: sandbox.stub(), - onDidChangeState: sandbox.stub(), - } - - // Mock AmazonQLspAuth - mockAuth = { - refreshConnection: sandbox.stub().resolves(), - } - - // Mock AuthUtil - authUtilStub = sandbox.stub(AuthUtil, 'instance').get(() => ({ - isConnectionValid: sandbox.stub().returns(true), - regionProfileManager: { - activeRegionProfile: { arn: 'test-profile-arn' }, - }, - auth: { - getConnectionState: sandbox.stub().returns('valid'), - activeConnection: { id: 'test-connection' }, - }, - })) - - // Create logger stub - loggerStub = { - info: sandbox.stub(), - debug: sandbox.stub(), - warn: sandbox.stub(), - error: sandbox.stub(), - } - - // Clear all relevant module caches - const sharedModuleId = require.resolve('aws-core-vscode/shared') - const configModuleId = require.resolve('../../../../src/lsp/config') - delete require.cache[sharedModuleId] - delete require.cache[configModuleId] - - // jscpd:ignore-start - // Create getLogger stub - getLoggerStub = sandbox.stub().returns(loggerStub) - - // Create a mock shared module with stubbed getLogger - const mockSharedModule = { - getLogger: getLoggerStub, - } - - // Override the require cache with our mock - require.cache[sharedModuleId] = { - id: sharedModuleId, - filename: sharedModuleId, - loaded: true, - parent: undefined, - children: [], - exports: mockSharedModule, - paths: [], - } as any - // jscpd:ignore-end - - // Mock pushConfigUpdate - pushConfigUpdateStub = sandbox.stub().resolves() - const mockConfigModule = { - pushConfigUpdate: pushConfigUpdateStub, - } - - require.cache[configModuleId] = { - id: configModuleId, - filename: configModuleId, - loaded: true, - parent: undefined, - children: [], - exports: mockConfigModule, - paths: [], - } as any - }) - - afterEach(() => { - sandbox.restore() - }) - - describe('initializeLanguageServerConfiguration behavior', function () { - it('should initialize configuration when connection is valid', async function () { - // Test the expected behavior of the function - const mockInitializeFunction = async (client: LanguageClient, context: string) => { - const { getLogger } = require('aws-core-vscode/shared') - const { pushConfigUpdate } = require('../../../../src/lsp/config') - const logger = getLogger('amazonqLsp') - - if (AuthUtil.instance.isConnectionValid()) { - logger.info(`[${context}] Initializing language server configuration`) - - // Send profile configuration - logger.debug(`[${context}] Sending profile configuration to language server`) - await pushConfigUpdate(client, { - type: 'profile', - profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, - }) - logger.debug(`[${context}] Profile configuration sent successfully`) - - // Send customization configuration - logger.debug(`[${context}] Sending customization configuration to language server`) - await pushConfigUpdate(client, { - type: 'customization', - customization: 'test-customization', - }) - logger.debug(`[${context}] Customization configuration sent successfully`) - - logger.info(`[${context}] Language server configuration completed successfully`) - } else { - logger.warn(`[${context}] Connection invalid, skipping configuration`) - } - } - - await mockInitializeFunction(mockClient as any, 'startup') - - // Verify logging - assert(loggerStub.info.calledWith('[startup] Initializing language server configuration')) - assert(loggerStub.debug.calledWith('[startup] Sending profile configuration to language server')) - assert(loggerStub.debug.calledWith('[startup] Profile configuration sent successfully')) - assert(loggerStub.debug.calledWith('[startup] Sending customization configuration to language server')) - assert(loggerStub.debug.calledWith('[startup] Customization configuration sent successfully')) - assert(loggerStub.info.calledWith('[startup] Language server configuration completed successfully')) - - // Verify pushConfigUpdate was called twice - assert.strictEqual(pushConfigUpdateStub.callCount, 2) - - // Verify profile configuration - assert( - pushConfigUpdateStub.calledWith(mockClient, { - type: 'profile', - profileArn: 'test-profile-arn', - }) - ) - - // Verify customization configuration - assert( - pushConfigUpdateStub.calledWith(mockClient, { - type: 'customization', - customization: 'test-customization', - }) - ) - }) - - it('should log warning when connection is invalid', async function () { - // Mock invalid connection - authUtilStub.get(() => ({ - isConnectionValid: sandbox.stub().returns(false), - auth: { - getConnectionState: sandbox.stub().returns('invalid'), - activeConnection: { id: 'test-connection' }, - }, - })) - - const mockInitializeFunction = async (client: LanguageClient, context: string) => { - const { getLogger } = require('aws-core-vscode/shared') - const logger = getLogger('amazonqLsp') - - // jscpd:ignore-start - if (AuthUtil.instance.isConnectionValid()) { - // Should not reach here - } else { - logger.warn( - `[${context}] Connection invalid, skipping language server configuration - this will cause authentication failures` - ) - const activeConnection = AuthUtil.instance.auth.activeConnection - const connectionState = activeConnection - ? AuthUtil.instance.auth.getConnectionState(activeConnection) - : 'no-connection' - logger.warn(`[${context}] Connection state: ${connectionState}`) - // jscpd:ignore-end - } - } - - await mockInitializeFunction(mockClient as any, 'crash-recovery') - - // Verify warning logs - assert( - loggerStub.warn.calledWith( - '[crash-recovery] Connection invalid, skipping language server configuration - this will cause authentication failures' - ) - ) - assert(loggerStub.warn.calledWith('[crash-recovery] Connection state: invalid')) - - // Verify pushConfigUpdate was not called - assert.strictEqual(pushConfigUpdateStub.callCount, 0) - }) - }) - - describe('crash recovery handler behavior', function () { - it('should reinitialize authentication after crash', async function () { - const mockCrashHandler = async (client: LanguageClient, auth: AmazonQLspAuth) => { - const { getLogger } = require('aws-core-vscode/shared') - const { pushConfigUpdate } = require('../../../../src/lsp/config') - const logger = getLogger('amazonqLsp') - - logger.info('[crash-recovery] Language server crash detected, reinitializing authentication') - - try { - logger.debug('[crash-recovery] Refreshing connection and sending bearer token') - await auth.refreshConnection(true) - logger.debug('[crash-recovery] Bearer token sent successfully') - - // Mock the configuration initialization - if (AuthUtil.instance.isConnectionValid()) { - await pushConfigUpdate(client, { - type: 'profile', - profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, - }) - } - - logger.info('[crash-recovery] Authentication reinitialized successfully') - } catch (error) { - logger.error(`[crash-recovery] Failed to reinitialize after crash: ${error}`) - } - } - - await mockCrashHandler(mockClient as any, mockAuth as any) - - // Verify crash recovery logging - assert( - loggerStub.info.calledWith( - '[crash-recovery] Language server crash detected, reinitializing authentication' - ) - ) - assert(loggerStub.debug.calledWith('[crash-recovery] Refreshing connection and sending bearer token')) - assert(loggerStub.debug.calledWith('[crash-recovery] Bearer token sent successfully')) - assert(loggerStub.info.calledWith('[crash-recovery] Authentication reinitialized successfully')) - - // Verify auth.refreshConnection was called - assert(mockAuth.refreshConnection.calledWith(true)) - - // Verify profile configuration was sent - assert( - pushConfigUpdateStub.calledWith(mockClient, { - type: 'profile', - profileArn: 'test-profile-arn', - }) - ) - }) - }) -}) diff --git a/packages/amazonq/test/unit/amazonq/lsp/config.test.ts b/packages/amazonq/test/unit/amazonq/lsp/config.test.ts index c31e873e181..69b15d6e311 100644 --- a/packages/amazonq/test/unit/amazonq/lsp/config.test.ts +++ b/packages/amazonq/test/unit/amazonq/lsp/config.test.ts @@ -77,151 +77,3 @@ describe('getAmazonQLspConfig', () => { delete process.env.__AMAZONQLSP_UI } }) - -describe('pushConfigUpdate', () => { - let sandbox: sinon.SinonSandbox - let mockClient: any - let loggerStub: any - let getLoggerStub: sinon.SinonStub - let pushConfigUpdate: any - - beforeEach(() => { - sandbox = sinon.createSandbox() - - // Mock LanguageClient - mockClient = { - sendRequest: sandbox.stub().resolves(), - sendNotification: sandbox.stub(), - } - - // Create logger stub - loggerStub = { - debug: sandbox.stub(), - } - - // Clear all relevant module caches - const configModuleId = require.resolve('../../../../src/lsp/config') - const sharedModuleId = require.resolve('aws-core-vscode/shared') - delete require.cache[configModuleId] - delete require.cache[sharedModuleId] - - // jscpd:ignore-start - // Create getLogger stub and store reference for test verification - getLoggerStub = sandbox.stub().returns(loggerStub) - - // Create a mock shared module with stubbed getLogger - const mockSharedModule = { - getLogger: getLoggerStub, - } - - // Override the require cache with our mock - require.cache[sharedModuleId] = { - id: sharedModuleId, - filename: sharedModuleId, - loaded: true, - parent: undefined, - children: [], - exports: mockSharedModule, - paths: [], - } as any - - // Now require the module - it should use our mocked getLogger - // jscpd:ignore-end - const configModule = require('../../../../src/lsp/config') - pushConfigUpdate = configModule.pushConfigUpdate - }) - - afterEach(() => { - sandbox.restore() - }) - - it('should send profile configuration with logging', async () => { - const config = { - type: 'profile' as const, - profileArn: 'test-profile-arn', - } - - await pushConfigUpdate(mockClient, config) - - // Verify logging - assert(loggerStub.debug.calledWith('Pushing profile configuration: test-profile-arn')) - assert(loggerStub.debug.calledWith('Profile configuration pushed successfully')) - - // Verify client call - assert(mockClient.sendRequest.calledOnce) - assert( - mockClient.sendRequest.calledWith(sinon.match.string, { - section: 'aws.q', - settings: { profileArn: 'test-profile-arn' }, - }) - ) - }) - - it('should send customization configuration with logging', async () => { - const config = { - type: 'customization' as const, - customization: 'test-customization-arn', - } - - await pushConfigUpdate(mockClient, config) - - // Verify logging - assert(loggerStub.debug.calledWith('Pushing customization configuration: test-customization-arn')) - assert(loggerStub.debug.calledWith('Customization configuration pushed successfully')) - - // Verify client call - assert(mockClient.sendNotification.calledOnce) - assert( - mockClient.sendNotification.calledWith(sinon.match.string, { - section: 'aws.q', - settings: { customization: 'test-customization-arn' }, - }) - ) - }) - - it('should handle undefined profile ARN', async () => { - const config = { - type: 'profile' as const, - profileArn: undefined, - } - - await pushConfigUpdate(mockClient, config) - - // Verify logging with undefined - assert(loggerStub.debug.calledWith('Pushing profile configuration: undefined')) - assert(loggerStub.debug.calledWith('Profile configuration pushed successfully')) - }) - - it('should handle undefined customization ARN', async () => { - const config = { - type: 'customization' as const, - customization: undefined, - } - - await pushConfigUpdate(mockClient, config) - - // Verify logging with undefined - assert(loggerStub.debug.calledWith('Pushing customization configuration: undefined')) - assert(loggerStub.debug.calledWith('Customization configuration pushed successfully')) - }) - - it('should send logLevel configuration with logging', async () => { - const config = { - type: 'logLevel' as const, - } - - await pushConfigUpdate(mockClient, config) - - // Verify logging - assert(loggerStub.debug.calledWith('Pushing log level configuration')) - assert(loggerStub.debug.calledWith('Log level configuration pushed successfully')) - - // Verify client call - assert(mockClient.sendNotification.calledOnce) - assert( - mockClient.sendNotification.calledWith(sinon.match.string, { - section: 'aws.logLevel', - }) - ) - }) -}) diff --git a/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts b/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts index 8a625fe3544..3160f69fa95 100644 --- a/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts +++ b/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts @@ -52,7 +52,6 @@ describe('showEdits', function () { delete require.cache[moduleId] delete require.cache[sharedModuleId] - // jscpd:ignore-start // Create getLogger stub and store reference for test verification getLoggerStub = sandbox.stub().returns(loggerStub) @@ -73,7 +72,6 @@ describe('showEdits', function () { } as any // Now require the module - it should use our mocked getLogger - // jscpd:ignore-end const imageRendererModule = require('../../../../../src/app/inline/EditRendering/imageRenderer') showEdits = imageRendererModule.showEdits diff --git a/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts b/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts index 7709eed10fe..9c1bb751a35 100644 --- a/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts +++ b/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts @@ -21,41 +21,17 @@ describe('securityIssueHoverProvider', () => { token = new vscode.CancellationTokenSource() }) - function buildCommandLink( - command: string, - commandIcon: string, - args: any[], - label: string, - tooltip: string - ): string { - return `[$(${commandIcon}) ${label}](command:${command}?${encodeURIComponent(JSON.stringify(args))} '${tooltip}')` + function buildCommandLink(command: string, args: any[], label: string, tooltip: string): string { + return `[$(${command.includes('ignore') ? 'error' : 'comment'}) ${label}](command:${command}?${encodeURIComponent(JSON.stringify(args))} '${tooltip}')` } function buildExpectedContent(issue: any, fileName: string, description: string, severity?: string): string { const severityBadge = severity ? ` ![${severity}](severity-${severity.toLowerCase()}.svg)` : ' ' const commands = [ - buildCommandLink( - 'aws.amazonq.explainIssue', - 'comment', - [issue, fileName], - 'Explain', - 'Explain with Amazon Q' - ), - buildCommandLink('aws.amazonq.generateFix', 'wrench', [issue, fileName], 'Fix', 'Fix with Amazon Q'), - buildCommandLink( - 'aws.amazonq.security.ignore', - 'error', - [issue, fileName, 'hover'], - 'Ignore', - 'Ignore Issue' - ), - buildCommandLink( - 'aws.amazonq.security.ignoreAll', - 'error', - [issue, 'hover'], - 'Ignore All', - 'Ignore Similar Issues' - ), + buildCommandLink('aws.amazonq.explainIssue', [issue, fileName], 'Explain', 'Explain with Amazon Q'), + buildCommandLink('aws.amazonq.generateFix', [issue, fileName], 'Fix', 'Fix with Amazon Q'), + buildCommandLink('aws.amazonq.security.ignore', [issue, fileName, 'hover'], 'Ignore', 'Ignore Issue'), + buildCommandLink('aws.amazonq.security.ignoreAll', [issue, 'hover'], 'Ignore All', 'Ignore Similar Issues'), ] return `## title${severityBadge}\n${description}\n\n${commands.join('\n | ')}\n` } diff --git a/packages/core/package.json b/packages/core/package.json index d446a1bdf41..6f8d27ef4dc 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -471,8 +471,8 @@ "@aws-sdk/types": "^3.13.1", "@aws/chat-client": "^0.1.4", "@aws/chat-client-ui-types": "^0.1.47", - "@aws/language-server-runtimes": "^0.2.111", - "@aws/language-server-runtimes-types": "^0.1.47", + "@aws/language-server-runtimes": "^0.2.102", + "@aws/language-server-runtimes-types": "^0.1.43", "@cspotcode/source-map-support": "^0.8.1", "@sinonjs/fake-timers": "^10.0.2", "@types/adm-zip": "^0.4.34", diff --git a/packages/core/src/auth/activation.ts b/packages/core/src/auth/activation.ts index 8305610dff7..5c48124c468 100644 --- a/packages/core/src/auth/activation.ts +++ b/packages/core/src/auth/activation.ts @@ -12,7 +12,7 @@ import { isAmazonQ, isSageMaker } from '../shared/extensionUtilities' import { getLogger } from '../shared/logger/logger' import { getErrorMsg } from '../shared/errors' -export interface SagemakerCookie { +interface SagemakerCookie { authMode?: 'Sso' | 'Iam' } diff --git a/packages/core/src/codewhisperer/commands/basicCommands.ts b/packages/core/src/codewhisperer/commands/basicCommands.ts index efe993356bd..745fe1a45a9 100644 --- a/packages/core/src/codewhisperer/commands/basicCommands.ts +++ b/packages/core/src/codewhisperer/commands/basicCommands.ts @@ -634,12 +634,6 @@ const registerToolkitApiCallbackOnce = once(() => { export const registerToolkitApiCallback = Commands.declare( { id: 'aws.amazonq.refreshConnectionCallback' }, () => async (toolkitApi?: any) => { - // Early return if already registered to avoid duplicate work - if (_toolkitApi) { - getLogger().debug('Toolkit API callback already registered, skipping') - return - } - // While the Q/CW exposes an API for the Toolkit to register callbacks on auth changes, // we need to do it manually here because the Toolkit would have been unable to call // this API if the Q/CW extension started afterwards (and this code block is running). diff --git a/packages/core/src/codewhisperer/region/regionProfileManager.ts b/packages/core/src/codewhisperer/region/regionProfileManager.ts index 24d58d7f588..e463321be19 100644 --- a/packages/core/src/codewhisperer/region/regionProfileManager.ts +++ b/packages/core/src/codewhisperer/region/regionProfileManager.ts @@ -69,7 +69,7 @@ export class RegionProfileManager { constructor(private readonly profileProvider: () => Promise) { super( 'aws.amazonq.regionProfiles.cache', - 3600000, + 60000, { resource: { locked: false, @@ -77,7 +77,7 @@ export class RegionProfileManager { result: undefined, }, }, - { timeout: 15000, interval: 500, truthy: true } + { timeout: 15000, interval: 1500, truthy: true } ) } diff --git a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts index bb9fe2cafa4..c907f99abe3 100644 --- a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts +++ b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts @@ -90,7 +90,7 @@ export class SecurityIssueHoverProvider implements vscode.HoverProvider { const generateFixCommand = this._getCommandMarkdown( 'aws.amazonq.generateFix', [issue, filePath], - 'wrench', + 'comment', 'Fix', 'Fix with Amazon Q' ) diff --git a/packages/core/src/shared/featureConfig.ts b/packages/core/src/shared/featureConfig.ts index c7b111b3243..d7acb9657be 100644 --- a/packages/core/src/shared/featureConfig.ts +++ b/packages/core/src/shared/featureConfig.ts @@ -55,9 +55,6 @@ export const featureDefinitions = new Map([ export class FeatureConfigProvider { private featureConfigs = new Map() - private fetchPromise: Promise | undefined = undefined - private lastFetchTime = 0 - private readonly minFetchInterval = 5000 // 5 seconds minimum between fetches static #instance: FeatureConfigProvider @@ -126,28 +123,6 @@ export class FeatureConfigProvider { return } - // Debounce multiple concurrent calls - const now = performance.now() - if (this.fetchPromise && now - this.lastFetchTime < this.minFetchInterval) { - getLogger().debug('amazonq: Debouncing feature config fetch') - return this.fetchPromise - } - - if (this.fetchPromise) { - return this.fetchPromise - } - - this.lastFetchTime = now - this.fetchPromise = this._fetchFeatureConfigsInternal() - - try { - await this.fetchPromise - } finally { - this.fetchPromise = undefined - } - } - - private async _fetchFeatureConfigsInternal(): Promise { getLogger().debug('amazonq: Fetching feature configs') try { const response = await this.listFeatureEvaluations() diff --git a/packages/core/src/shared/utilities/index.ts b/packages/core/src/shared/utilities/index.ts index 18d86da4d55..ecf753090ca 100644 --- a/packages/core/src/shared/utilities/index.ts +++ b/packages/core/src/shared/utilities/index.ts @@ -7,4 +7,3 @@ export { isExtensionInstalled, isExtensionActive } from './vsCodeUtils' export { VSCODE_EXTENSION_ID } from '../extensions' export * from './functionUtils' export * as messageUtils from './messages' -export * as CommentUtils from './commentUtils' diff --git a/packages/core/src/shared/utilities/resourceCache.ts b/packages/core/src/shared/utilities/resourceCache.ts index a399dea66ca..c0beee61cd6 100644 --- a/packages/core/src/shared/utilities/resourceCache.ts +++ b/packages/core/src/shared/utilities/resourceCache.ts @@ -60,21 +60,6 @@ export abstract class CachedResource { abstract resourceProvider(): Promise async getResource(): Promise { - // Check cache without locking first - const quickCheck = this.readCacheOrDefault() - if (quickCheck.resource.result && !quickCheck.resource.locked) { - const duration = now() - quickCheck.resource.timestamp - if (duration < this.expirationInMilli) { - logger.debug( - `cache hit (fast path), duration(%sms) is less than expiration(%sms), returning cached value: %s`, - duration, - this.expirationInMilli, - this.key - ) - return quickCheck.resource.result - } - } - const cachedValue = await this.tryLoadResourceAndLock() const resource = cachedValue?.resource diff --git a/packages/core/src/test/auth/activation.test.ts b/packages/core/src/test/auth/activation.test.ts deleted file mode 100644 index f203033acba..00000000000 --- a/packages/core/src/test/auth/activation.test.ts +++ /dev/null @@ -1,146 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import * as sinon from 'sinon' -import assert from 'assert' -import { initialize, SagemakerCookie } from '../../auth/activation' -import { LoginManager } from '../../auth/deprecated/loginManager' -import * as extensionUtilities from '../../shared/extensionUtilities' -import * as authUtils from '../../auth/utils' -import * as errors from '../../shared/errors' - -describe('auth/activation', function () { - let sandbox: sinon.SinonSandbox - let mockLoginManager: LoginManager - let executeCommandStub: sinon.SinonStub - let isAmazonQStub: sinon.SinonStub - let isSageMakerStub: sinon.SinonStub - let initializeCredentialsProviderManagerStub: sinon.SinonStub - let getErrorMsgStub: sinon.SinonStub - let mockLogger: any - - beforeEach(function () { - sandbox = sinon.createSandbox() - - // Create mocks - mockLoginManager = { - login: sandbox.stub(), - logout: sandbox.stub(), - } as any - - mockLogger = { - warn: sandbox.stub(), - info: sandbox.stub(), - error: sandbox.stub(), - debug: sandbox.stub(), - } - - // Stub external dependencies - executeCommandStub = sandbox.stub(vscode.commands, 'executeCommand') - isAmazonQStub = sandbox.stub(extensionUtilities, 'isAmazonQ') - isSageMakerStub = sandbox.stub(extensionUtilities, 'isSageMaker') - initializeCredentialsProviderManagerStub = sandbox.stub(authUtils, 'initializeCredentialsProviderManager') - getErrorMsgStub = sandbox.stub(errors, 'getErrorMsg') - }) - - afterEach(function () { - sandbox.restore() - }) - - describe('initialize', function () { - it('should not execute sagemaker.parseCookies when not in AmazonQ and SageMaker environment', async function () { - isAmazonQStub.returns(false) - isSageMakerStub.returns(false) - - await initialize(mockLoginManager) - - assert.ok(!executeCommandStub.called) - assert.ok(!initializeCredentialsProviderManagerStub.called) - }) - - it('should not execute sagemaker.parseCookies when only in AmazonQ environment', async function () { - isAmazonQStub.returns(true) - isSageMakerStub.returns(false) - - await initialize(mockLoginManager) - - assert.ok(!executeCommandStub.called) - assert.ok(!initializeCredentialsProviderManagerStub.called) - }) - - it('should not execute sagemaker.parseCookies when only in SageMaker environment', async function () { - isAmazonQStub.returns(false) - isSageMakerStub.returns(true) - - await initialize(mockLoginManager) - - assert.ok(!executeCommandStub.called) - assert.ok(!initializeCredentialsProviderManagerStub.called) - }) - - it('should execute sagemaker.parseCookies when in both AmazonQ and SageMaker environment', async function () { - isAmazonQStub.returns(true) - isSageMakerStub.returns(true) - executeCommandStub.withArgs('sagemaker.parseCookies').resolves({ authMode: 'Sso' } as SagemakerCookie) - - await initialize(mockLoginManager) - - assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) - assert.ok(!initializeCredentialsProviderManagerStub.called) - }) - - it('should initialize credentials provider manager when authMode is not Sso', async function () { - isAmazonQStub.returns(true) - isSageMakerStub.returns(true) - executeCommandStub.withArgs('sagemaker.parseCookies').resolves({ authMode: 'Iam' } as SagemakerCookie) - - await initialize(mockLoginManager) - - assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) - assert.ok(initializeCredentialsProviderManagerStub.calledOnce) - }) - - it('should initialize credentials provider manager when authMode is undefined', async function () { - isAmazonQStub.returns(true) - isSageMakerStub.returns(true) - executeCommandStub.withArgs('sagemaker.parseCookies').resolves({} as SagemakerCookie) - - await initialize(mockLoginManager) - - assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) - assert.ok(initializeCredentialsProviderManagerStub.calledOnce) - }) - - it('should warn and not throw when sagemaker.parseCookies command is not found', async function () { - isAmazonQStub.returns(true) - isSageMakerStub.returns(true) - const error = new Error("command 'sagemaker.parseCookies' not found") - executeCommandStub.withArgs('sagemaker.parseCookies').rejects(error) - getErrorMsgStub.returns("command 'sagemaker.parseCookies' not found") - - await initialize(mockLoginManager) - - assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) - assert.ok(getErrorMsgStub.calledOnceWith(error)) - assert.ok(!initializeCredentialsProviderManagerStub.called) - }) - - it('should throw when sagemaker.parseCookies fails with non-command-not-found error', async function () { - isAmazonQStub.returns(true) - isSageMakerStub.returns(true) - const error = new Error('Some other error') - executeCommandStub.withArgs('sagemaker.parseCookies').rejects(error) - getErrorMsgStub.returns('Some other error') - - await assert.rejects(initialize(mockLoginManager), /Some other error/) - - assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) - assert.ok(getErrorMsgStub.calledOnceWith(error)) - assert.ok(!mockLogger.warn.called) - assert.ok(!initializeCredentialsProviderManagerStub.called) - }) - }) -}) diff --git a/packages/core/src/test/lambda/utils.test.ts b/packages/core/src/test/lambda/utils.test.ts index a3eebe043a7..975738edeba 100644 --- a/packages/core/src/test/lambda/utils.test.ts +++ b/packages/core/src/test/lambda/utils.test.ts @@ -13,7 +13,6 @@ import { setFunctionInfo, compareCodeSha, } from '../../lambda/utils' -import { LambdaFunction } from '../../lambda/commands/uploadLambda' import { DefaultLambdaClient } from '../../shared/clients/lambdaClient' import { fs } from '../../shared/fs/fs' import { tempDirPath } from '../../shared/filesystemUtilities' @@ -117,21 +116,9 @@ describe('lambda utils', function () { }) describe('setFunctionInfo', function () { - let mockLambda: LambdaFunction - - // jscpd:ignore-start - beforeEach(function () { - mockLambda = { - name: 'test-function', - region: 'us-east-1', - configuration: { FunctionName: 'test-function' }, - } - }) - afterEach(function () { sinon.restore() }) - // jscpd:ignore-end it('merges with existing data', async function () { const existingData = { lastDeployed: 123456, undeployed: true, sha: 'old-sha', handlerFile: 'index.js' } @@ -153,21 +140,9 @@ describe('lambda utils', function () { }) describe('compareCodeSha', function () { - let mockLambda: LambdaFunction - - // jscpd:ignore-start - beforeEach(function () { - mockLambda = { - name: 'test-function', - region: 'us-east-1', - configuration: { FunctionName: 'test-function' }, - } - }) - afterEach(function () { sinon.restore() }) - // jscpd:ignore-end it('returns true when local and remote SHA match', async function () { sinon.stub(fs, 'readFileText').resolves(JSON.stringify({ sha: 'same-sha' })) From ca66d7951a6bb3d0c341552c721c4304401abb4c Mon Sep 17 00:00:00 2001 From: Laxman Reddy <141967714+laileni-aws@users.noreply.github.com> Date: Fri, 18 Jul 2025 20:00:58 -0700 Subject: [PATCH 35/69] fix(amazonq): removing unwanted files (#7715) [chore: removing unwanted files](https://github.com/aws/aws-toolkit-vscode/commit/bd2d5fe8b8d5f126631fbcb187a6f8c5b19373bb) --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Co-authored-by: Na Yue --- package-lock.json | 2 +- packages/amazonq/.changes/1.84.0.json | 18 ++++++++++++++++++ ...x-45aef014-07f3-4511-a9f6-d7233077784c.json | 4 ---- ...x-91380b87-5955-4c15-b762-31e7f1c71575.json | 4 ---- ...e-9e413673-5ef6-4920-97b1-e73635f3a0f5.json | 4 ---- packages/amazonq/CHANGELOG.md | 6 ++++++ packages/amazonq/package.json | 2 +- 7 files changed, 26 insertions(+), 14 deletions(-) create mode 100644 packages/amazonq/.changes/1.84.0.json delete mode 100644 packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json delete mode 100644 packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json delete mode 100644 packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json diff --git a/package-lock.json b/package-lock.json index ed21305ffee..e2b2ebb5920 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29954,7 +29954,7 @@ }, "packages/amazonq": { "name": "amazon-q-vscode", - "version": "1.84.0-SNAPSHOT", + "version": "1.85.0-SNAPSHOT", "license": "Apache-2.0", "dependencies": { "aws-core-vscode": "file:../core/" diff --git a/packages/amazonq/.changes/1.84.0.json b/packages/amazonq/.changes/1.84.0.json new file mode 100644 index 00000000000..e73a685e054 --- /dev/null +++ b/packages/amazonq/.changes/1.84.0.json @@ -0,0 +1,18 @@ +{ + "date": "2025-07-17", + "version": "1.84.0", + "entries": [ + { + "type": "Bug Fix", + "description": "Slightly delay rendering inline completion when user is typing" + }, + { + "type": "Bug Fix", + "description": "Render first response before receiving all paginated inline completion results" + }, + { + "type": "Feature", + "description": "Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab." + } + ] +} \ No newline at end of file diff --git a/packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json b/packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json deleted file mode 100644 index 4d45af73411..00000000000 --- a/packages/amazonq/.changes/next-release/Bug Fix-45aef014-07f3-4511-a9f6-d7233077784c.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "Slightly delay rendering inline completion when user is typing" -} diff --git a/packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json b/packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json deleted file mode 100644 index 72293c3b97a..00000000000 --- a/packages/amazonq/.changes/next-release/Bug Fix-91380b87-5955-4c15-b762-31e7f1c71575.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Bug Fix", - "description": "Render first response before receiving all paginated inline completion results" -} diff --git a/packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json b/packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json deleted file mode 100644 index af699a24355..00000000000 --- a/packages/amazonq/.changes/next-release/Feature-9e413673-5ef6-4920-97b1-e73635f3a0f5.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "Feature", - "description": "Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab." -} diff --git a/packages/amazonq/CHANGELOG.md b/packages/amazonq/CHANGELOG.md index 980abde9d63..ccf3fb8a215 100644 --- a/packages/amazonq/CHANGELOG.md +++ b/packages/amazonq/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.84.0 2025-07-17 + +- **Bug Fix** Slightly delay rendering inline completion when user is typing +- **Bug Fix** Render first response before receiving all paginated inline completion results +- **Feature** Explain and Fix for any issue in Code Issues panel will pull the experience into chat. Also no more view details tab. + ## 1.83.0 2025-07-09 - **Feature** Amazon Q /test, /doc, and /dev capabilities integrated into Agentic coding. diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index 25ab19b6ffb..fd83354aca8 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -2,7 +2,7 @@ "name": "amazon-q-vscode", "displayName": "Amazon Q", "description": "The most capable generative AI-powered assistant for building, operating, and transforming software, with advanced capabilities for managing data and AI", - "version": "1.84.0-SNAPSHOT", + "version": "1.85.0-SNAPSHOT", "extensionKind": [ "workspace" ], From 9facfddb5439252b4ec347d90db14c19ce769bdd Mon Sep 17 00:00:00 2001 From: aws-toolkit-automation <> Date: Sat, 19 Jul 2025 03:05:36 +0000 Subject: [PATCH 36/69] Release 1.85.0 --- package-lock.json | 4 ++-- packages/amazonq/.changes/1.85.0.json | 5 +++++ packages/amazonq/CHANGELOG.md | 4 ++++ packages/amazonq/package.json | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 packages/amazonq/.changes/1.85.0.json diff --git a/package-lock.json b/package-lock.json index e2b2ebb5920..c4d02d96c25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "prettier": "^3.3.3", "prettier-plugin-sh": "^0.14.0", "pretty-quick": "^4.0.0", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "typescript": "^5.0.4", "webpack": "^5.95.0", "webpack-cli": "^5.1.4", @@ -29954,7 +29954,7 @@ }, "packages/amazonq": { "name": "amazon-q-vscode", - "version": "1.85.0-SNAPSHOT", + "version": "1.85.0", "license": "Apache-2.0", "dependencies": { "aws-core-vscode": "file:../core/" diff --git a/packages/amazonq/.changes/1.85.0.json b/packages/amazonq/.changes/1.85.0.json new file mode 100644 index 00000000000..b0aba38025b --- /dev/null +++ b/packages/amazonq/.changes/1.85.0.json @@ -0,0 +1,5 @@ +{ + "date": "2025-07-19", + "version": "1.85.0", + "entries": [] +} \ No newline at end of file diff --git a/packages/amazonq/CHANGELOG.md b/packages/amazonq/CHANGELOG.md index ccf3fb8a215..d96b350db8d 100644 --- a/packages/amazonq/CHANGELOG.md +++ b/packages/amazonq/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.85.0 2025-07-19 + +- Miscellaneous non-user-facing changes + ## 1.84.0 2025-07-17 - **Bug Fix** Slightly delay rendering inline completion when user is typing diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index fd83354aca8..fc350b690e0 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -2,7 +2,7 @@ "name": "amazon-q-vscode", "displayName": "Amazon Q", "description": "The most capable generative AI-powered assistant for building, operating, and transforming software, with advanced capabilities for managing data and AI", - "version": "1.85.0-SNAPSHOT", + "version": "1.85.0", "extensionKind": [ "workspace" ], From 6c7f0409d51be627c3ec35871a2abb1bbd3e3912 Mon Sep 17 00:00:00 2001 From: aws-toolkit-automation <> Date: Sat, 19 Jul 2025 03:57:04 +0000 Subject: [PATCH 37/69] Update version to snapshot version: 1.86.0-SNAPSHOT --- package-lock.json | 4 ++-- packages/amazonq/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c4d02d96c25..01d671d07c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "prettier": "^3.3.3", "prettier-plugin-sh": "^0.14.0", "pretty-quick": "^4.0.0", - "ts-node": "^10.9.2", + "ts-node": "^10.9.1", "typescript": "^5.0.4", "webpack": "^5.95.0", "webpack-cli": "^5.1.4", @@ -29954,7 +29954,7 @@ }, "packages/amazonq": { "name": "amazon-q-vscode", - "version": "1.85.0", + "version": "1.86.0-SNAPSHOT", "license": "Apache-2.0", "dependencies": { "aws-core-vscode": "file:../core/" diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index fc350b690e0..a550b4702bf 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -2,7 +2,7 @@ "name": "amazon-q-vscode", "displayName": "Amazon Q", "description": "The most capable generative AI-powered assistant for building, operating, and transforming software, with advanced capabilities for managing data and AI", - "version": "1.85.0", + "version": "1.86.0-SNAPSHOT", "extensionKind": [ "workspace" ], From e19352551990c4e4c94fb5301304e9d06addef46 Mon Sep 17 00:00:00 2001 From: Nitish Kumar Singh Date: Sun, 20 Jul 2025 20:00:44 -0700 Subject: [PATCH 38/69] deps: bump @aws-toolkits/telemetry to 1.0.329 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 01d671d07c8..bf51f9800f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "vscode-nls-dev": "^4.0.4" }, "devDependencies": { - "@aws-toolkits/telemetry": "^1.0.328", + "@aws-toolkits/telemetry": "^1.0.329", "@playwright/browser-chromium": "^1.43.1", "@stylistic/eslint-plugin": "^2.11.0", "@types/he": "^1.2.3", @@ -15008,9 +15008,9 @@ } }, "node_modules/@aws-toolkits/telemetry": { - "version": "1.0.328", - "resolved": "https://registry.npmjs.org/@aws-toolkits/telemetry/-/telemetry-1.0.328.tgz", - "integrity": "sha512-DenImMbYXCqyh8ofX6nh8IINHRXlELdi3BycvEefy0By6hEUao+BuW92SLfbqJ7Z+BgRrwminI91au5aGe9RHA==", + "version": "1.0.329", + "resolved": "https://registry.npmjs.org/@aws-toolkits/telemetry/-/telemetry-1.0.329.tgz", + "integrity": "sha512-zMkljZDtIAxuZzPTLL5zIxn+zGmk767sbqGIc2ZYuv0sSU+UoYgB3tqwV5KVV2oDPKs5593nwJC97NVHJqzowQ==", "dev": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index 4eef95171e2..b84e4b8c361 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "skippedTestReport": "ts-node ./scripts/skippedTestReport.ts ./packages/amazonq/test/e2e/" }, "devDependencies": { - "@aws-toolkits/telemetry": "^1.0.328", + "@aws-toolkits/telemetry": "^1.0.329", "@playwright/browser-chromium": "^1.43.1", "@stylistic/eslint-plugin": "^2.11.0", "@types/he": "^1.2.3", From 2770a81e051b3f28aa53a891403387f207d04d46 Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Mon, 21 Jul 2025 10:27:23 -0700 Subject: [PATCH 39/69] fix(amazonq): handle suppress single finding in agentic reviewer --- packages/amazonq/src/lsp/chat/messages.ts | 17 +++++++++++++---- packages/core/src/shared/utilities/index.ts | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index 737b77dbeb2..607c3d7bdc0 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -95,6 +95,7 @@ import { decryptResponse, encryptRequest } from '../encryption' import { getCursorState } from '../utils' import { focusAmazonQPanel } from './commands' import { ChatMessage } from '@aws/language-server-runtimes/server-interface' +import { CommentUtils } from 'aws-core-vscode/utils' export function registerActiveEditorChangeListener(languageClient: LanguageClient) { let debounceTimer: NodeJS.Timeout | undefined @@ -701,7 +702,7 @@ async function handleCompleteResult( ) { const decryptedMessage = await decryptResponse(result, encryptionKey) - handleSecurityFindings(decryptedMessage, languageClient) + await handleSecurityFindings(decryptedMessage, languageClient) void provider.webview?.postMessage({ command: chatRequestType.method, @@ -716,10 +717,10 @@ async function handleCompleteResult( disposable.dispose() } -function handleSecurityFindings( +async function handleSecurityFindings( decryptedMessage: { additionalMessages?: ChatMessage[] }, languageClient: LanguageClient -): void { +): Promise { if (decryptedMessage.additionalMessages === undefined || decryptedMessage.additionalMessages.length === 0) { return } @@ -730,10 +731,18 @@ function handleSecurityFindings( try { const aggregatedCodeScanIssues: AggregatedCodeScanIssue[] = JSON.parse(message.body) for (const aggregatedCodeScanIssue of aggregatedCodeScanIssues) { + const document = await vscode.workspace.openTextDocument(aggregatedCodeScanIssue.filePath) for (const issue of aggregatedCodeScanIssue.issues) { - issue.visible = !CodeWhispererSettings.instance + const isIssueTitleIgnored = CodeWhispererSettings.instance .getIgnoredSecurityIssues() .includes(issue.title) + const isSingleIssueIgnored = CommentUtils.detectCommentAboveLine( + document, + issue.startLine, + CodeWhispererConstants.amazonqIgnoreNextLine + ) + + issue.visible = !isIssueTitleIgnored && !isSingleIssueIgnored } } initSecurityScanRender(aggregatedCodeScanIssues, undefined, CodeAnalysisScope.PROJECT) diff --git a/packages/core/src/shared/utilities/index.ts b/packages/core/src/shared/utilities/index.ts index ecf753090ca..18d86da4d55 100644 --- a/packages/core/src/shared/utilities/index.ts +++ b/packages/core/src/shared/utilities/index.ts @@ -7,3 +7,4 @@ export { isExtensionInstalled, isExtensionActive } from './vsCodeUtils' export { VSCODE_EXTENSION_ID } from '../extensions' export * from './functionUtils' export * as messageUtils from './messages' +export * as CommentUtils from './commentUtils' From 06b4df694b68c2bdfa7fae2729f817d110676ee1 Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Mon, 21 Jul 2025 10:30:00 -0700 Subject: [PATCH 40/69] fix(amazonq): changed the icon for security issue hover fix option to keep it consistent in all places --- .../securityIssueHoverProvider.test.ts | 36 +++++++++++++++---- .../service/securityIssueHoverProvider.ts | 2 +- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts b/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts index 9c1bb751a35..7709eed10fe 100644 --- a/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts +++ b/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts @@ -21,17 +21,41 @@ describe('securityIssueHoverProvider', () => { token = new vscode.CancellationTokenSource() }) - function buildCommandLink(command: string, args: any[], label: string, tooltip: string): string { - return `[$(${command.includes('ignore') ? 'error' : 'comment'}) ${label}](command:${command}?${encodeURIComponent(JSON.stringify(args))} '${tooltip}')` + function buildCommandLink( + command: string, + commandIcon: string, + args: any[], + label: string, + tooltip: string + ): string { + return `[$(${commandIcon}) ${label}](command:${command}?${encodeURIComponent(JSON.stringify(args))} '${tooltip}')` } function buildExpectedContent(issue: any, fileName: string, description: string, severity?: string): string { const severityBadge = severity ? ` ![${severity}](severity-${severity.toLowerCase()}.svg)` : ' ' const commands = [ - buildCommandLink('aws.amazonq.explainIssue', [issue, fileName], 'Explain', 'Explain with Amazon Q'), - buildCommandLink('aws.amazonq.generateFix', [issue, fileName], 'Fix', 'Fix with Amazon Q'), - buildCommandLink('aws.amazonq.security.ignore', [issue, fileName, 'hover'], 'Ignore', 'Ignore Issue'), - buildCommandLink('aws.amazonq.security.ignoreAll', [issue, 'hover'], 'Ignore All', 'Ignore Similar Issues'), + buildCommandLink( + 'aws.amazonq.explainIssue', + 'comment', + [issue, fileName], + 'Explain', + 'Explain with Amazon Q' + ), + buildCommandLink('aws.amazonq.generateFix', 'wrench', [issue, fileName], 'Fix', 'Fix with Amazon Q'), + buildCommandLink( + 'aws.amazonq.security.ignore', + 'error', + [issue, fileName, 'hover'], + 'Ignore', + 'Ignore Issue' + ), + buildCommandLink( + 'aws.amazonq.security.ignoreAll', + 'error', + [issue, 'hover'], + 'Ignore All', + 'Ignore Similar Issues' + ), ] return `## title${severityBadge}\n${description}\n\n${commands.join('\n | ')}\n` } diff --git a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts index c907f99abe3..bb9fe2cafa4 100644 --- a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts +++ b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts @@ -90,7 +90,7 @@ export class SecurityIssueHoverProvider implements vscode.HoverProvider { const generateFixCommand = this._getCommandMarkdown( 'aws.amazonq.generateFix', [issue, filePath], - 'comment', + 'wrench', 'Fix', 'Fix with Amazon Q' ) From 34a66756aa139c0a062e40b0df7766e969a54d8a Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Mon, 21 Jul 2025 16:38:10 -0700 Subject: [PATCH 41/69] fix(amazonq): disable codeReviewInChat feature flag --- packages/amazonq/src/lsp/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index 9ad635f17fd..ce83938d158 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -168,7 +168,7 @@ export async function startLanguageServer( reroute: true, modelSelection: true, workspaceFilePath: vscode.workspace.workspaceFile?.fsPath, - codeReviewInChat: true, + codeReviewInChat: false, }, window: { notifications: true, From 9562ccc9381e0bd8618463a5365c81b5fdca571d Mon Sep 17 00:00:00 2001 From: Na Yue Date: Tue, 22 Jul 2025 10:04:21 -0700 Subject: [PATCH 42/69] fix(amazonq): reverting for Amazon Q (#7714) (#7730) ## Problem This reverts commit ab7fb6ad170f2f7df71d2912dedfa22c3620553c. ## Solution --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- package-lock.json | 18 +- ...-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json | 4 + ...-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json | 4 + .../app/inline/EditRendering/imageRenderer.ts | 6 + packages/amazonq/src/app/inline/completion.ts | 15 +- .../src/app/inline/documentEventListener.ts | 19 ++ .../src/app/inline/recommendationService.ts | 15 +- packages/amazonq/src/extension.ts | 25 +- packages/amazonq/src/lsp/chat/messages.ts | 17 +- packages/amazonq/src/lsp/client.ts | 115 ++++++-- packages/amazonq/src/lsp/config.ts | 10 +- .../amazonq/src/lsp/rotatingLogChannel.ts | 246 ++++++++++++++++ .../src/test/rotatingLogChannel.test.ts | 192 +++++++++++++ .../apps/inline/recommendationService.test.ts | 2 + .../test/unit/amazonq/lsp/client.test.ts | 268 ++++++++++++++++++ .../test/unit/amazonq/lsp/config.test.ts | 148 ++++++++++ .../EditRendering/imageRenderer.test.ts | 2 + .../securityIssueHoverProvider.test.ts | 36 ++- packages/core/package.json | 4 +- packages/core/src/auth/activation.ts | 2 +- .../codewhisperer/commands/basicCommands.ts | 6 + .../region/regionProfileManager.ts | 4 +- .../service/securityIssueHoverProvider.ts | 2 +- packages/core/src/shared/featureConfig.ts | 25 ++ packages/core/src/shared/utilities/index.ts | 1 + .../src/shared/utilities/resourceCache.ts | 15 + .../core/src/test/auth/activation.test.ts | 146 ++++++++++ packages/core/src/test/lambda/utils.test.ts | 25 ++ 28 files changed, 1301 insertions(+), 71 deletions(-) create mode 100644 packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json create mode 100644 packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json create mode 100644 packages/amazonq/src/lsp/rotatingLogChannel.ts create mode 100644 packages/amazonq/src/test/rotatingLogChannel.test.ts create mode 100644 packages/amazonq/test/unit/amazonq/lsp/client.test.ts create mode 100644 packages/core/src/test/auth/activation.test.ts diff --git a/package-lock.json b/package-lock.json index 01d671d07c8..35e80921caf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15044,13 +15044,13 @@ } }, "node_modules/@aws/language-server-runtimes": { - "version": "0.2.102", - "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes/-/language-server-runtimes-0.2.102.tgz", - "integrity": "sha512-O68zmXClLP6mtKxh0fzGKYW3MwgFCTkAgL32WKzOWLwD6gMc5CaVRrNsZ2cabkAudf2laTeWeSDZJZsiQ0hCfA==", + "version": "0.2.111", + "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes/-/language-server-runtimes-0.2.111.tgz", + "integrity": "sha512-eIHKzWkLTTb3qUCeT2nIrpP99dEv/OiUOcPB00MNCsOPWBBO/IoZhfGRNrE8+stgZMQkKLFH2ZYxn3ByB6OsCQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws/language-server-runtimes-types": "^0.1.43", + "@aws/language-server-runtimes-types": "^0.1.47", "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.200.0", "@opentelemetry/core": "^2.0.0", @@ -15077,9 +15077,9 @@ } }, "node_modules/@aws/language-server-runtimes-types": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes-types/-/language-server-runtimes-types-0.1.43.tgz", - "integrity": "sha512-qXaAGkiJ1hldF+Ynu6ZBXS18s47UOnbZEHxKiGRrBlBX2L75ih/4yasj8ITgshqS5Kx5JMntu+8vpc0CkGV6jA==", + "version": "0.1.47", + "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes-types/-/language-server-runtimes-types-0.1.47.tgz", + "integrity": "sha512-l5dOdx/MR3SO0HYXkSL9fcR05f4Aw7qRMuASMdWOK93LOSZeANPVOGIWblRnoJejfYiPXcufCFyjLnGpATExag==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -30063,8 +30063,8 @@ "@aws-sdk/types": "^3.13.1", "@aws/chat-client": "^0.1.4", "@aws/chat-client-ui-types": "^0.1.47", - "@aws/language-server-runtimes": "^0.2.102", - "@aws/language-server-runtimes-types": "^0.1.43", + "@aws/language-server-runtimes": "^0.2.111", + "@aws/language-server-runtimes-types": "^0.1.47", "@cspotcode/source-map-support": "^0.8.1", "@sinonjs/fake-timers": "^10.0.2", "@types/adm-zip": "^0.4.34", diff --git a/packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json b/packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json new file mode 100644 index 00000000000..1a9e5c32e6d --- /dev/null +++ b/packages/amazonq/.changes/next-release/Bug Fix-316fb610-0ea9-40d1-bdb7-d371a6be4a4e.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Let Enter invoke auto completion more consistently" +} diff --git a/packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json b/packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json new file mode 100644 index 00000000000..f2234549a0d --- /dev/null +++ b/packages/amazonq/.changes/next-release/Bug Fix-9d694e40-7fc7-4504-b08c-6b22a5ebcb1c.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Use documentChangeEvent as auto trigger condition" +} diff --git a/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts b/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts index 9af3878ef82..195879ff779 100644 --- a/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts +++ b/packages/amazonq/src/app/inline/EditRendering/imageRenderer.ts @@ -29,6 +29,12 @@ export async function showEdits( const { svgImage, startLine, newCode, origionalCodeHighlightRange } = await svgGenerationService.generateDiffSvg(currentFile, item.insertText as string) + // TODO: To investigate why it fails and patch [generateDiffSvg] + if (newCode.length === 0) { + getLogger('nextEditPrediction').warn('not able to apply provided edit suggestion, skip rendering') + return + } + if (svgImage) { // display the SVG image await displaySvgDecoration( diff --git a/packages/amazonq/src/app/inline/completion.ts b/packages/amazonq/src/app/inline/completion.ts index 360be53e67a..9020deac824 100644 --- a/packages/amazonq/src/app/inline/completion.ts +++ b/packages/amazonq/src/app/inline/completion.ts @@ -241,6 +241,12 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem return [] } + const isAutoTrigger = context.triggerKind === InlineCompletionTriggerKind.Automatic + if (isAutoTrigger && !CodeSuggestionsState.instance.isSuggestionsEnabled()) { + // return early when suggestions are disabled with auto trigger + return [] + } + // yield event loop to let the document listen catch updates await sleep(1) // prevent user deletion invoking auto trigger @@ -254,12 +260,6 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem try { const t0 = performance.now() vsCodeState.isRecommendationsActive = true - const isAutoTrigger = context.triggerKind === InlineCompletionTriggerKind.Automatic - if (isAutoTrigger && !CodeSuggestionsState.instance.isSuggestionsEnabled()) { - // return early when suggestions are disabled with auto trigger - return [] - } - // handling previous session const prevSession = this.sessionManager.getActiveSession() const prevSessionId = prevSession?.sessionId @@ -335,7 +335,8 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem context, token, isAutoTrigger, - getAllRecommendationsOptions + getAllRecommendationsOptions, + this.documentEventListener.getLastDocumentChangeEvent(document.uri.fsPath)?.event ) // get active item from session for displaying const items = this.sessionManager.getActiveRecommendation() diff --git a/packages/amazonq/src/app/inline/documentEventListener.ts b/packages/amazonq/src/app/inline/documentEventListener.ts index 4e60b595ce2..36f65dc7331 100644 --- a/packages/amazonq/src/app/inline/documentEventListener.ts +++ b/packages/amazonq/src/app/inline/documentEventListener.ts @@ -21,6 +21,11 @@ export class DocumentEventListener { this.lastDocumentChangeEventMap.clear() } this.lastDocumentChangeEventMap.set(e.document.uri.fsPath, { event: e, timestamp: performance.now() }) + // The VS Code provideInlineCompletionCallback may not trigger when Enter is pressed, especially in Python files + // manually make this trigger. In case of duplicate, the provideInlineCompletionCallback is already debounced + if (this.isEnter(e) && vscode.window.activeTextEditor) { + void vscode.commands.executeCommand('editor.action.inlineSuggest.trigger') + } } }) } @@ -47,4 +52,18 @@ export class DocumentEventListener { this.documentChangeListener.dispose() } } + + private isEnter(e: vscode.TextDocumentChangeEvent): boolean { + if (e.contentChanges.length !== 1) { + return false + } + const str = e.contentChanges[0].text + if (str.length === 0) { + return false + } + return ( + (str.startsWith('\r\n') && str.substring(2).trim() === '') || + (str[0] === '\n' && str.substring(1).trim() === '') + ) + } } diff --git a/packages/amazonq/src/app/inline/recommendationService.ts b/packages/amazonq/src/app/inline/recommendationService.ts index ddde310999f..1329c68a51c 100644 --- a/packages/amazonq/src/app/inline/recommendationService.ts +++ b/packages/amazonq/src/app/inline/recommendationService.ts @@ -2,10 +2,12 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ +import * as vscode from 'vscode' import { InlineCompletionListWithReferences, InlineCompletionWithReferencesParams, inlineCompletionWithReferencesRequestType, + TextDocumentContentChangeEvent, } from '@aws/language-server-runtimes/protocol' import { CancellationToken, InlineCompletionContext, Position, TextDocument } from 'vscode' import { LanguageClient } from 'vscode-languageclient' @@ -40,10 +42,20 @@ export class RecommendationService { context: InlineCompletionContext, token: CancellationToken, isAutoTrigger: boolean, - options: GetAllRecommendationsOptions = { emitTelemetry: true, showUi: true } + options: GetAllRecommendationsOptions = { emitTelemetry: true, showUi: true }, + documentChangeEvent?: vscode.TextDocumentChangeEvent ) { // Record that a regular request is being made this.cursorUpdateRecorder?.recordCompletionRequest() + const documentChangeParams = documentChangeEvent + ? { + textDocument: { + uri: document.uri.toString(), + version: document.version, + }, + contentChanges: documentChangeEvent.contentChanges.map((x) => x as TextDocumentContentChangeEvent), + } + : undefined let request: InlineCompletionWithReferencesParams = { textDocument: { @@ -51,6 +63,7 @@ export class RecommendationService { }, position, context, + documentChangeParams: documentChangeParams, } if (options.editsStreakToken) { request = { ...request, partialResultToken: options.editsStreakToken } diff --git a/packages/amazonq/src/extension.ts b/packages/amazonq/src/extension.ts index 9ca13136eab..1e26724ff61 100644 --- a/packages/amazonq/src/extension.ts +++ b/packages/amazonq/src/extension.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Auth, AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth' +import { AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth' import { activate as activateCodeWhisperer, shutdown as shutdownCodeWhisperer } from 'aws-core-vscode/codewhisperer' import { makeEndpointsProvider, registerGenericCommands } from 'aws-core-vscode' import { CommonAuthWebview } from 'aws-core-vscode/login' @@ -44,8 +44,8 @@ import * as vscode from 'vscode' import { registerCommands } from './commands' import { focusAmazonQPanel } from 'aws-core-vscode/codewhispererChat' import { activate as activateAmazonqLsp } from './lsp/activation' -import { activate as activateInlineCompletion } from './app/inline/activation' import { hasGlibcPatch } from './lsp/client' +import { RotatingLogChannel } from './lsp/rotatingLogChannel' export const amazonQContextPrefix = 'amazonq' @@ -104,7 +104,12 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is globals.manifestPaths.endpoints = context.asAbsolutePath(join('resources', 'endpoints.json')) globals.regionProvider = RegionProvider.fromEndpointsProvider(makeEndpointsProvider()) - const qLogChannel = vscode.window.createOutputChannel('Amazon Q Logs', { log: true }) + // Create rotating log channel for all Amazon Q logs + const qLogChannel = new RotatingLogChannel( + 'Amazon Q Logs', + context, + vscode.window.createOutputChannel('Amazon Q Logs', { log: true }) + ) await activateLogger(context, amazonQContextPrefix, qLogChannel) globals.logOutputChannel = qLogChannel globals.loginManager = new LoginManager(globals.awsContext, new CredentialsStore()) @@ -113,6 +118,8 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is getLogger().error('fs.init: invalid env vars found: %O', homeDirLogs) } + getLogger().info('Rotating logger has been setup') + await activateTelemetry(context, globals.awsContext, Settings.instance, 'Amazon Q For VS Code') await initializeAuth(globals.loginManager) @@ -126,17 +133,11 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is // This contains every lsp agnostic things (auth, security scan, code scan) await activateCodeWhisperer(extContext as ExtContext) - if ( - (Experiments.instance.get('amazonqLSP', true) || Auth.instance.isInternalAmazonUser()) && - (!isAmazonLinux2() || hasGlibcPatch()) - ) { - // start the Amazon Q LSP for internal users first - // for AL2, start LSP if glibc patch is found + + if (!isAmazonLinux2() || hasGlibcPatch()) { + // Activate Amazon Q LSP for everyone unless they're using AL2 without the glibc patch await activateAmazonqLsp(context) } - if (!Experiments.instance.get('amazonqLSPInline', true)) { - await activateInlineCompletion() - } // Generic extension commands registerGenericCommands(context, amazonQContextPrefix) diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index 9841c7edee9..f869bbe0da3 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -95,6 +95,7 @@ import { decryptResponse, encryptRequest } from '../encryption' import { getCursorState } from '../utils' import { focusAmazonQPanel } from './commands' import { ChatMessage } from '@aws/language-server-runtimes/server-interface' +import { CommentUtils } from 'aws-core-vscode/utils' export function registerActiveEditorChangeListener(languageClient: LanguageClient) { let debounceTimer: NodeJS.Timeout | undefined @@ -701,7 +702,7 @@ async function handleCompleteResult( ) { const decryptedMessage = await decryptResponse(result, encryptionKey) - handleSecurityFindings(decryptedMessage, languageClient) + await handleSecurityFindings(decryptedMessage, languageClient) void provider.webview?.postMessage({ command: chatRequestType.method, @@ -716,10 +717,10 @@ async function handleCompleteResult( disposable.dispose() } -function handleSecurityFindings( +async function handleSecurityFindings( decryptedMessage: { additionalMessages?: ChatMessage[] }, languageClient: LanguageClient -): void { +): Promise { if (decryptedMessage.additionalMessages === undefined || decryptedMessage.additionalMessages.length === 0) { return } @@ -730,10 +731,18 @@ function handleSecurityFindings( try { const aggregatedCodeScanIssues: AggregatedCodeScanIssue[] = JSON.parse(message.body) for (const aggregatedCodeScanIssue of aggregatedCodeScanIssues) { + const document = await vscode.workspace.openTextDocument(aggregatedCodeScanIssue.filePath) for (const issue of aggregatedCodeScanIssue.issues) { - issue.visible = !CodeWhispererSettings.instance + const isIssueTitleIgnored = CodeWhispererSettings.instance .getIgnoredSecurityIssues() .includes(issue.title) + const isSingleIssueIgnored = CommentUtils.detectCommentAboveLine( + document, + issue.startLine, + CodeWhispererConstants.amazonqIgnoreNextLine + ) + + issue.visible = !isIssueTitleIgnored && !isSingleIssueIgnored } } initSecurityScanRender(aggregatedCodeScanIssues, undefined, CodeAnalysisScope.PROJECT) diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index e6ef1e5dd9c..e94842123ac 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -8,6 +8,7 @@ import * as nls from 'vscode-nls' import { LanguageClient, LanguageClientOptions, RequestType, State } from 'vscode-languageclient' import { InlineCompletionManager } from '../app/inline/completion' import { AmazonQLspAuth, encryptionKey, notificationTypes } from './auth' +import { RotatingLogChannel } from './rotatingLogChannel' import { CreateFilesParams, DeleteFilesParams, @@ -94,6 +95,23 @@ export async function startLanguageServer( const clientId = 'amazonq' const traceServerEnabled = Settings.instance.isSet(`${clientId}.trace.server`) + + // Create custom output channel that writes to disk but sends UI output to the appropriate channel + const lspLogChannel = new RotatingLogChannel( + traceServerEnabled ? 'Amazon Q Language Server' : 'Amazon Q Logs', + extensionContext, + traceServerEnabled + ? vscode.window.createOutputChannel('Amazon Q Language Server', { log: true }) + : globals.logOutputChannel + ) + + // Add cleanup for our file output channel + toDispose.push({ + dispose: () => { + lspLogChannel.dispose() + }, + }) + let executable: string[] = [] // apply the GLIBC 2.28 path to node js runtime binary if (isSageMaker()) { @@ -168,6 +186,7 @@ export async function startLanguageServer( reroute: true, modelSelection: true, workspaceFilePath: vscode.workspace.workspaceFile?.fsPath, + qCodeReviewInChat: true, }, window: { notifications: true, @@ -190,15 +209,9 @@ export async function startLanguageServer( }, }, /** - * When the trace server is enabled it outputs a ton of log messages so: - * When trace server is enabled, logs go to a seperate "Amazon Q Language Server" output. - * Otherwise, logs go to the regular "Amazon Q Logs" channel. + * Using our RotatingLogger for all logs */ - ...(traceServerEnabled - ? {} - : { - outputChannel: globals.logOutputChannel, - }), + outputChannel: lspLogChannel, } const client = new LanguageClient( @@ -251,6 +264,59 @@ async function initializeAuth(client: LanguageClient): Promise { return auth } +// jscpd:ignore-start +async function initializeLanguageServerConfiguration(client: LanguageClient, context: string = 'startup') { + const logger = getLogger('amazonqLsp') + + if (AuthUtil.instance.isConnectionValid()) { + logger.info(`[${context}] Initializing language server configuration`) + // jscpd:ignore-end + + try { + // Send profile configuration + logger.debug(`[${context}] Sending profile configuration to language server`) + await sendProfileToLsp(client) + logger.debug(`[${context}] Profile configuration sent successfully`) + + // Send customization configuration + logger.debug(`[${context}] Sending customization configuration to language server`) + await pushConfigUpdate(client, { + type: 'customization', + customization: getSelectedCustomization(), + }) + logger.debug(`[${context}] Customization configuration sent successfully`) + + logger.info(`[${context}] Language server configuration completed successfully`) + } catch (error) { + logger.error(`[${context}] Failed to initialize language server configuration: ${error}`) + throw error + } + } else { + logger.warn( + `[${context}] Connection invalid, skipping language server configuration - this will cause authentication failures` + ) + const activeConnection = AuthUtil.instance.auth.activeConnection + const connectionState = activeConnection + ? AuthUtil.instance.auth.getConnectionState(activeConnection) + : 'no-connection' + logger.warn(`[${context}] Connection state: ${connectionState}`) + } +} + +async function sendProfileToLsp(client: LanguageClient) { + const logger = getLogger('amazonqLsp') + const profileArn = AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn + + logger.debug(`Sending profile to LSP: ${profileArn || 'undefined'}`) + + await pushConfigUpdate(client, { + type: 'profile', + profileArn: profileArn, + }) + + logger.debug(`Profile sent to LSP successfully`) +} + async function onLanguageServerReady( extensionContext: vscode.ExtensionContext, auth: AmazonQLspAuth, @@ -282,14 +348,7 @@ async function onLanguageServerReady( // We manually push the cached values the first time since event handlers, which should push, may not have been setup yet. // Execution order is weird and should be fixed in the flare implementation. // TODO: Revisit if we need this if we setup the event handlers properly - if (AuthUtil.instance.isConnectionValid()) { - await sendProfileToLsp(client) - - await pushConfigUpdate(client, { - type: 'customization', - customization: getSelectedCustomization(), - }) - } + await initializeLanguageServerConfiguration(client, 'startup') toDispose.push( inlineManager, @@ -391,13 +450,6 @@ async function onLanguageServerReady( // Set this inside onReady so that it only triggers on subsequent language server starts (not the first) onServerRestartHandler(client, auth) ) - - async function sendProfileToLsp(client: LanguageClient) { - await pushConfigUpdate(client, { - type: 'profile', - profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, - }) - } } /** @@ -417,8 +469,21 @@ function onServerRestartHandler(client: LanguageClient, auth: AmazonQLspAuth) { // TODO: Port this metric override to common definitions telemetry.languageServer_crash.emit({ id: 'AmazonQ' }) - // Need to set the auth token in the again - await auth.refreshConnection(true) + const logger = getLogger('amazonqLsp') + logger.info('[crash-recovery] Language server crash detected, reinitializing authentication') + + try { + // Send bearer token + logger.debug('[crash-recovery] Refreshing connection and sending bearer token') + await auth.refreshConnection(true) + logger.debug('[crash-recovery] Bearer token sent successfully') + + // Send profile and customization configuration + await initializeLanguageServerConfiguration(client, 'crash-recovery') + logger.info('[crash-recovery] Authentication reinitialized successfully') + } catch (error) { + logger.error(`[crash-recovery] Failed to reinitialize after crash: ${error}`) + } }) } diff --git a/packages/amazonq/src/lsp/config.ts b/packages/amazonq/src/lsp/config.ts index 66edc9ff6f1..6b88eb98d21 100644 --- a/packages/amazonq/src/lsp/config.ts +++ b/packages/amazonq/src/lsp/config.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ import * as vscode from 'vscode' -import { DevSettings, getServiceEnvVarConfig, BaseLspInstaller } from 'aws-core-vscode/shared' +import { DevSettings, getServiceEnvVarConfig, BaseLspInstaller, getLogger } from 'aws-core-vscode/shared' import { LanguageClient } from 'vscode-languageclient' import { DidChangeConfigurationNotification, @@ -68,23 +68,31 @@ export function toAmazonQLSPLogLevel(logLevel: vscode.LogLevel): LspLogLevel { * push the given config. */ export async function pushConfigUpdate(client: LanguageClient, config: QConfigs) { + const logger = getLogger('amazonqLsp') + switch (config.type) { case 'profile': + logger.debug(`Pushing profile configuration: ${config.profileArn || 'undefined'}`) await client.sendRequest(updateConfigurationRequestType.method, { section: 'aws.q', settings: { profileArn: config.profileArn }, }) + logger.debug(`Profile configuration pushed successfully`) break case 'customization': + logger.debug(`Pushing customization configuration: ${config.customization || 'undefined'}`) client.sendNotification(DidChangeConfigurationNotification.type.method, { section: 'aws.q', settings: { customization: config.customization }, }) + logger.debug(`Customization configuration pushed successfully`) break case 'logLevel': + logger.debug(`Pushing log level configuration`) client.sendNotification(DidChangeConfigurationNotification.type.method, { section: 'aws.logLevel', }) + logger.debug(`Log level configuration pushed successfully`) break } } diff --git a/packages/amazonq/src/lsp/rotatingLogChannel.ts b/packages/amazonq/src/lsp/rotatingLogChannel.ts new file mode 100644 index 00000000000..b8e3df276f9 --- /dev/null +++ b/packages/amazonq/src/lsp/rotatingLogChannel.ts @@ -0,0 +1,246 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as path from 'path' +import * as fs from 'fs' // eslint-disable-line no-restricted-imports +import { getLogger } from 'aws-core-vscode/shared' + +export class RotatingLogChannel implements vscode.LogOutputChannel { + private fileStream: fs.WriteStream | undefined + private originalChannel: vscode.LogOutputChannel + private logger = getLogger('amazonqLsp') + private currentFileSize = 0 + // eslint-disable-next-line @typescript-eslint/naming-convention + private readonly MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB + // eslint-disable-next-line @typescript-eslint/naming-convention + private readonly MAX_LOG_FILES = 4 + private static currentLogPath: string | undefined + + private static generateNewLogPath(logDir: string): string { + const timestamp = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '-').replace('Z', '') + return path.join(logDir, `amazonq-lsp-${timestamp}.log`) + } + + constructor( + public readonly name: string, + private readonly extensionContext: vscode.ExtensionContext, + outputChannel: vscode.LogOutputChannel + ) { + this.originalChannel = outputChannel + this.initFileStream() + } + + private async cleanupOldLogs(): Promise { + try { + const logDir = this.extensionContext.storageUri?.fsPath + if (!logDir) { + return + } + + // Get all log files + const files = await fs.promises.readdir(logDir) + const logFiles = files + .filter((f) => f.startsWith('amazonq-lsp-') && f.endsWith('.log')) + .map((f) => ({ + name: f, + path: path.join(logDir, f), + time: fs.statSync(path.join(logDir, f)).mtime.getTime(), + })) + .sort((a, b) => b.time - a.time) // Sort newest to oldest + + // Remove all but the most recent MAX_LOG_FILES files + for (const file of logFiles.slice(this.MAX_LOG_FILES - 1)) { + try { + await fs.promises.unlink(file.path) + this.logger.debug(`Removed old log file: ${file.path}`) + } catch (err) { + this.logger.error(`Failed to remove old log file ${file.path}: ${err}`) + } + } + } catch (err) { + this.logger.error(`Failed to cleanup old logs: ${err}`) + } + } + + private getLogFilePath(): string { + // If we already have a path, reuse it + if (RotatingLogChannel.currentLogPath) { + return RotatingLogChannel.currentLogPath + } + + const logDir = this.extensionContext.storageUri?.fsPath + if (!logDir) { + throw new Error('No storage URI available') + } + + // Generate initial path + RotatingLogChannel.currentLogPath = RotatingLogChannel.generateNewLogPath(logDir) + return RotatingLogChannel.currentLogPath + } + + private async rotateLog(): Promise { + try { + // Close current stream + if (this.fileStream) { + this.fileStream.end() + } + + const logDir = this.extensionContext.storageUri?.fsPath + if (!logDir) { + throw new Error('No storage URI available') + } + + // Generate new path directly + RotatingLogChannel.currentLogPath = RotatingLogChannel.generateNewLogPath(logDir) + + // Create new log file with new path + this.fileStream = fs.createWriteStream(RotatingLogChannel.currentLogPath, { flags: 'a' }) + this.currentFileSize = 0 + + // Clean up old files + await this.cleanupOldLogs() + + this.logger.info(`Created new log file: ${RotatingLogChannel.currentLogPath}`) + } catch (err) { + this.logger.error(`Failed to rotate log file: ${err}`) + } + } + + private initFileStream() { + try { + const logDir = this.extensionContext.storageUri + if (!logDir) { + this.logger.error('Failed to get storage URI for logs') + return + } + + // Ensure directory exists + if (!fs.existsSync(logDir.fsPath)) { + fs.mkdirSync(logDir.fsPath, { recursive: true }) + } + + const logPath = this.getLogFilePath() + this.fileStream = fs.createWriteStream(logPath, { flags: 'a' }) + this.currentFileSize = 0 + this.logger.info(`Logging to file: ${logPath}`) + } catch (err) { + this.logger.error(`Failed to create log file: ${err}`) + } + } + + get logLevel(): vscode.LogLevel { + return this.originalChannel.logLevel + } + + get onDidChangeLogLevel(): vscode.Event { + return this.originalChannel.onDidChangeLogLevel + } + + trace(message: string, ...args: any[]): void { + this.originalChannel.trace(message, ...args) + this.writeToFile(`[TRACE] ${message}`) + } + + debug(message: string, ...args: any[]): void { + this.originalChannel.debug(message, ...args) + this.writeToFile(`[DEBUG] ${message}`) + } + + info(message: string, ...args: any[]): void { + this.originalChannel.info(message, ...args) + this.writeToFile(`[INFO] ${message}`) + } + + warn(message: string, ...args: any[]): void { + this.originalChannel.warn(message, ...args) + this.writeToFile(`[WARN] ${message}`) + } + + error(message: string | Error, ...args: any[]): void { + this.originalChannel.error(message, ...args) + this.writeToFile(`[ERROR] ${message instanceof Error ? message.stack || message.message : message}`) + } + + append(value: string): void { + this.originalChannel.append(value) + this.writeToFile(value) + } + + appendLine(value: string): void { + this.originalChannel.appendLine(value) + this.writeToFile(value + '\n') + } + + replace(value: string): void { + this.originalChannel.replace(value) + this.writeToFile(`[REPLACE] ${value}`) + } + + clear(): void { + this.originalChannel.clear() + } + + show(preserveFocus?: boolean): void + show(column?: vscode.ViewColumn, preserveFocus?: boolean): void + show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void { + if (typeof columnOrPreserveFocus === 'boolean') { + this.originalChannel.show(columnOrPreserveFocus) + } else { + this.originalChannel.show(columnOrPreserveFocus, preserveFocus) + } + } + + hide(): void { + this.originalChannel.hide() + } + + dispose(): void { + // First dispose the original channel + this.originalChannel.dispose() + + // Close our file stream if it exists + if (this.fileStream) { + this.fileStream.end() + } + + // Clean up all log files + const logDir = this.extensionContext.storageUri?.fsPath + if (logDir) { + try { + const files = fs.readdirSync(logDir) + for (const file of files) { + if (file.startsWith('amazonq-lsp-') && file.endsWith('.log')) { + fs.unlinkSync(path.join(logDir, file)) + } + } + this.logger.info('Cleaned up all log files during disposal') + } catch (err) { + this.logger.error(`Failed to cleanup log files during disposal: ${err}`) + } + } + } + + private writeToFile(content: string): void { + if (this.fileStream) { + try { + const timestamp = new Date().toISOString() + const logLine = `${timestamp} ${content}\n` + const size = Buffer.byteLength(logLine) + + // If this write would exceed max file size, rotate first + if (this.currentFileSize + size > this.MAX_FILE_SIZE) { + void this.rotateLog() + } + + this.fileStream.write(logLine) + this.currentFileSize += size + } catch (err) { + this.logger.error(`Failed to write to log file: ${err}`) + void this.rotateLog() + } + } + } +} diff --git a/packages/amazonq/src/test/rotatingLogChannel.test.ts b/packages/amazonq/src/test/rotatingLogChannel.test.ts new file mode 100644 index 00000000000..87c4c109603 --- /dev/null +++ b/packages/amazonq/src/test/rotatingLogChannel.test.ts @@ -0,0 +1,192 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +// eslint-disable-next-line no-restricted-imports +import * as fs from 'fs' +import * as path from 'path' +import * as assert from 'assert' +import { RotatingLogChannel } from '../lsp/rotatingLogChannel' + +describe('RotatingLogChannel', () => { + let testDir: string + let mockExtensionContext: vscode.ExtensionContext + let mockOutputChannel: vscode.LogOutputChannel + let logChannel: RotatingLogChannel + + beforeEach(() => { + // Create a temp test directory + testDir = fs.mkdtempSync('amazonq-test-logs-') + + // Mock extension context + mockExtensionContext = { + storageUri: { fsPath: testDir } as vscode.Uri, + } as vscode.ExtensionContext + + // Mock output channel + mockOutputChannel = { + name: 'Test Output Channel', + append: () => {}, + appendLine: () => {}, + replace: () => {}, + clear: () => {}, + show: () => {}, + hide: () => {}, + dispose: () => {}, + trace: () => {}, + debug: () => {}, + info: () => {}, + warn: () => {}, + error: () => {}, + logLevel: vscode.LogLevel.Info, + onDidChangeLogLevel: new vscode.EventEmitter().event, + } + + // Create log channel instance + logChannel = new RotatingLogChannel('test', mockExtensionContext, mockOutputChannel) + }) + + afterEach(() => { + // Cleanup test directory + if (fs.existsSync(testDir)) { + fs.rmSync(testDir, { recursive: true, force: true }) + } + }) + + it('creates log file on initialization', () => { + const files = fs.readdirSync(testDir) + assert.strictEqual(files.length, 1) + assert.ok(files[0].startsWith('amazonq-lsp-')) + assert.ok(files[0].endsWith('.log')) + }) + + it('writes logs to file', async () => { + const testMessage = 'test log message' + logChannel.info(testMessage) + + // Allow async operations to complete + await new Promise((resolve) => setTimeout(resolve, 100)) + + const files = fs.readdirSync(testDir) + const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') + assert.ok(content.includes(testMessage)) + }) + + it('rotates files when size limit is reached', async () => { + // Write enough data to trigger rotation + const largeMessage = 'x'.repeat(1024 * 1024) // 1MB + for (let i = 0; i < 6; i++) { + // Should create at least 2 files + logChannel.info(largeMessage) + } + + // Allow async operations to complete + await new Promise((resolve) => setTimeout(resolve, 100)) + + const files = fs.readdirSync(testDir) + assert.ok(files.length > 1, 'Should have created multiple log files') + assert.ok(files.length <= 4, 'Should not exceed max file limit') + }) + + it('keeps only the specified number of files', async () => { + // Write enough data to create more than MAX_LOG_FILES + const largeMessage = 'x'.repeat(1024 * 1024) // 1MB + for (let i = 0; i < 20; i++) { + // Should trigger multiple rotations + logChannel.info(largeMessage) + } + + // Allow async operations to complete + await new Promise((resolve) => setTimeout(resolve, 100)) + + const files = fs.readdirSync(testDir) + assert.strictEqual(files.length, 4, 'Should keep exactly 4 files') + }) + + it('cleans up all files on dispose', async () => { + // Write some logs + logChannel.info('test message') + + // Allow async operations to complete + await new Promise((resolve) => setTimeout(resolve, 100)) + + // Verify files exist + assert.ok(fs.readdirSync(testDir).length > 0) + + // Dispose + logChannel.dispose() + + // Allow async operations to complete + await new Promise((resolve) => setTimeout(resolve, 100)) + + // Verify files are cleaned up + const remainingFiles = fs.readdirSync(testDir).filter((f) => f.startsWith('amazonq-lsp-') && f.endsWith('.log')) + assert.strictEqual(remainingFiles.length, 0, 'Should have no log files after disposal') + }) + + it('includes timestamps in log messages', async () => { + const testMessage = 'test message' + logChannel.info(testMessage) + + // Allow async operations to complete + await new Promise((resolve) => setTimeout(resolve, 100)) + + const files = fs.readdirSync(testDir) + const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') + + // ISO date format regex + const timestampRegex = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/ + assert.ok(timestampRegex.test(content), 'Log entry should include ISO timestamp') + }) + + it('handles different log levels correctly', async () => { + const testMessage = 'test message' + logChannel.trace(testMessage) + logChannel.debug(testMessage) + logChannel.info(testMessage) + logChannel.warn(testMessage) + logChannel.error(testMessage) + + // Allow async operations to complete + await new Promise((resolve) => setTimeout(resolve, 100)) + + const files = fs.readdirSync(testDir) + const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') + + assert.ok(content.includes('[TRACE]'), 'Should include TRACE level') + assert.ok(content.includes('[DEBUG]'), 'Should include DEBUG level') + assert.ok(content.includes('[INFO]'), 'Should include INFO level') + assert.ok(content.includes('[WARN]'), 'Should include WARN level') + assert.ok(content.includes('[ERROR]'), 'Should include ERROR level') + }) + + it('delegates log level to the original channel', () => { + // Set up a mock output channel with a specific log level + const mockChannel = { + ...mockOutputChannel, + logLevel: vscode.LogLevel.Trace, + } + + // Create a new log channel with the mock + const testLogChannel = new RotatingLogChannel('test-delegate', mockExtensionContext, mockChannel) + + // Verify that the log level is delegated correctly + assert.strictEqual( + testLogChannel.logLevel, + vscode.LogLevel.Trace, + 'Should delegate log level to original channel' + ) + + // Change the mock's log level + mockChannel.logLevel = vscode.LogLevel.Debug + + // Verify that the change is reflected + assert.strictEqual( + testLogChannel.logLevel, + vscode.LogLevel.Debug, + 'Should reflect changes to original channel log level' + ) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts b/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts index 744fcc63c53..54eea8347c5 100644 --- a/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts +++ b/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts @@ -146,6 +146,7 @@ describe('RecommendationService', () => { }, position: mockPosition, context: mockContext, + documentChangeParams: undefined, }) // Verify session management @@ -187,6 +188,7 @@ describe('RecommendationService', () => { }, position: mockPosition, context: mockContext, + documentChangeParams: undefined, } const secondRequestArgs = sendRequestStub.secondCall.args[1] assert.deepStrictEqual(firstRequestArgs, expectedRequestArgs) diff --git a/packages/amazonq/test/unit/amazonq/lsp/client.test.ts b/packages/amazonq/test/unit/amazonq/lsp/client.test.ts new file mode 100644 index 00000000000..7c99c47e0ea --- /dev/null +++ b/packages/amazonq/test/unit/amazonq/lsp/client.test.ts @@ -0,0 +1,268 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import sinon from 'sinon' +import { LanguageClient } from 'vscode-languageclient' +import { AuthUtil } from 'aws-core-vscode/codewhisperer' +import { AmazonQLspAuth } from '../../../../src/lsp/auth' + +// These tests verify the behavior of the authentication functions +// Since the actual functions are module-level and use real dependencies, +// we test the expected behavior through mock implementations + +describe('Language Server Client Authentication', function () { + let sandbox: sinon.SinonSandbox + let mockClient: any + let mockAuth: any + let authUtilStub: sinon.SinonStub + let loggerStub: any + let getLoggerStub: sinon.SinonStub + let pushConfigUpdateStub: sinon.SinonStub + + beforeEach(() => { + sandbox = sinon.createSandbox() + + // Mock LanguageClient + mockClient = { + sendRequest: sandbox.stub().resolves(), + sendNotification: sandbox.stub(), + onDidChangeState: sandbox.stub(), + } + + // Mock AmazonQLspAuth + mockAuth = { + refreshConnection: sandbox.stub().resolves(), + } + + // Mock AuthUtil + authUtilStub = sandbox.stub(AuthUtil, 'instance').get(() => ({ + isConnectionValid: sandbox.stub().returns(true), + regionProfileManager: { + activeRegionProfile: { arn: 'test-profile-arn' }, + }, + auth: { + getConnectionState: sandbox.stub().returns('valid'), + activeConnection: { id: 'test-connection' }, + }, + })) + + // Create logger stub + loggerStub = { + info: sandbox.stub(), + debug: sandbox.stub(), + warn: sandbox.stub(), + error: sandbox.stub(), + } + + // Clear all relevant module caches + const sharedModuleId = require.resolve('aws-core-vscode/shared') + const configModuleId = require.resolve('../../../../src/lsp/config') + delete require.cache[sharedModuleId] + delete require.cache[configModuleId] + + // jscpd:ignore-start + // Create getLogger stub + getLoggerStub = sandbox.stub().returns(loggerStub) + + // Create a mock shared module with stubbed getLogger + const mockSharedModule = { + getLogger: getLoggerStub, + } + + // Override the require cache with our mock + require.cache[sharedModuleId] = { + id: sharedModuleId, + filename: sharedModuleId, + loaded: true, + parent: undefined, + children: [], + exports: mockSharedModule, + paths: [], + } as any + // jscpd:ignore-end + + // Mock pushConfigUpdate + pushConfigUpdateStub = sandbox.stub().resolves() + const mockConfigModule = { + pushConfigUpdate: pushConfigUpdateStub, + } + + require.cache[configModuleId] = { + id: configModuleId, + filename: configModuleId, + loaded: true, + parent: undefined, + children: [], + exports: mockConfigModule, + paths: [], + } as any + }) + + afterEach(() => { + sandbox.restore() + }) + + describe('initializeLanguageServerConfiguration behavior', function () { + it('should initialize configuration when connection is valid', async function () { + // Test the expected behavior of the function + const mockInitializeFunction = async (client: LanguageClient, context: string) => { + const { getLogger } = require('aws-core-vscode/shared') + const { pushConfigUpdate } = require('../../../../src/lsp/config') + const logger = getLogger('amazonqLsp') + + if (AuthUtil.instance.isConnectionValid()) { + logger.info(`[${context}] Initializing language server configuration`) + + // Send profile configuration + logger.debug(`[${context}] Sending profile configuration to language server`) + await pushConfigUpdate(client, { + type: 'profile', + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + logger.debug(`[${context}] Profile configuration sent successfully`) + + // Send customization configuration + logger.debug(`[${context}] Sending customization configuration to language server`) + await pushConfigUpdate(client, { + type: 'customization', + customization: 'test-customization', + }) + logger.debug(`[${context}] Customization configuration sent successfully`) + + logger.info(`[${context}] Language server configuration completed successfully`) + } else { + logger.warn(`[${context}] Connection invalid, skipping configuration`) + } + } + + await mockInitializeFunction(mockClient as any, 'startup') + + // Verify logging + assert(loggerStub.info.calledWith('[startup] Initializing language server configuration')) + assert(loggerStub.debug.calledWith('[startup] Sending profile configuration to language server')) + assert(loggerStub.debug.calledWith('[startup] Profile configuration sent successfully')) + assert(loggerStub.debug.calledWith('[startup] Sending customization configuration to language server')) + assert(loggerStub.debug.calledWith('[startup] Customization configuration sent successfully')) + assert(loggerStub.info.calledWith('[startup] Language server configuration completed successfully')) + + // Verify pushConfigUpdate was called twice + assert.strictEqual(pushConfigUpdateStub.callCount, 2) + + // Verify profile configuration + assert( + pushConfigUpdateStub.calledWith(mockClient, { + type: 'profile', + profileArn: 'test-profile-arn', + }) + ) + + // Verify customization configuration + assert( + pushConfigUpdateStub.calledWith(mockClient, { + type: 'customization', + customization: 'test-customization', + }) + ) + }) + + it('should log warning when connection is invalid', async function () { + // Mock invalid connection + authUtilStub.get(() => ({ + isConnectionValid: sandbox.stub().returns(false), + auth: { + getConnectionState: sandbox.stub().returns('invalid'), + activeConnection: { id: 'test-connection' }, + }, + })) + + const mockInitializeFunction = async (client: LanguageClient, context: string) => { + const { getLogger } = require('aws-core-vscode/shared') + const logger = getLogger('amazonqLsp') + + // jscpd:ignore-start + if (AuthUtil.instance.isConnectionValid()) { + // Should not reach here + } else { + logger.warn( + `[${context}] Connection invalid, skipping language server configuration - this will cause authentication failures` + ) + const activeConnection = AuthUtil.instance.auth.activeConnection + const connectionState = activeConnection + ? AuthUtil.instance.auth.getConnectionState(activeConnection) + : 'no-connection' + logger.warn(`[${context}] Connection state: ${connectionState}`) + // jscpd:ignore-end + } + } + + await mockInitializeFunction(mockClient as any, 'crash-recovery') + + // Verify warning logs + assert( + loggerStub.warn.calledWith( + '[crash-recovery] Connection invalid, skipping language server configuration - this will cause authentication failures' + ) + ) + assert(loggerStub.warn.calledWith('[crash-recovery] Connection state: invalid')) + + // Verify pushConfigUpdate was not called + assert.strictEqual(pushConfigUpdateStub.callCount, 0) + }) + }) + + describe('crash recovery handler behavior', function () { + it('should reinitialize authentication after crash', async function () { + const mockCrashHandler = async (client: LanguageClient, auth: AmazonQLspAuth) => { + const { getLogger } = require('aws-core-vscode/shared') + const { pushConfigUpdate } = require('../../../../src/lsp/config') + const logger = getLogger('amazonqLsp') + + logger.info('[crash-recovery] Language server crash detected, reinitializing authentication') + + try { + logger.debug('[crash-recovery] Refreshing connection and sending bearer token') + await auth.refreshConnection(true) + logger.debug('[crash-recovery] Bearer token sent successfully') + + // Mock the configuration initialization + if (AuthUtil.instance.isConnectionValid()) { + await pushConfigUpdate(client, { + type: 'profile', + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }) + } + + logger.info('[crash-recovery] Authentication reinitialized successfully') + } catch (error) { + logger.error(`[crash-recovery] Failed to reinitialize after crash: ${error}`) + } + } + + await mockCrashHandler(mockClient as any, mockAuth as any) + + // Verify crash recovery logging + assert( + loggerStub.info.calledWith( + '[crash-recovery] Language server crash detected, reinitializing authentication' + ) + ) + assert(loggerStub.debug.calledWith('[crash-recovery] Refreshing connection and sending bearer token')) + assert(loggerStub.debug.calledWith('[crash-recovery] Bearer token sent successfully')) + assert(loggerStub.info.calledWith('[crash-recovery] Authentication reinitialized successfully')) + + // Verify auth.refreshConnection was called + assert(mockAuth.refreshConnection.calledWith(true)) + + // Verify profile configuration was sent + assert( + pushConfigUpdateStub.calledWith(mockClient, { + type: 'profile', + profileArn: 'test-profile-arn', + }) + ) + }) + }) +}) diff --git a/packages/amazonq/test/unit/amazonq/lsp/config.test.ts b/packages/amazonq/test/unit/amazonq/lsp/config.test.ts index 69b15d6e311..c31e873e181 100644 --- a/packages/amazonq/test/unit/amazonq/lsp/config.test.ts +++ b/packages/amazonq/test/unit/amazonq/lsp/config.test.ts @@ -77,3 +77,151 @@ describe('getAmazonQLspConfig', () => { delete process.env.__AMAZONQLSP_UI } }) + +describe('pushConfigUpdate', () => { + let sandbox: sinon.SinonSandbox + let mockClient: any + let loggerStub: any + let getLoggerStub: sinon.SinonStub + let pushConfigUpdate: any + + beforeEach(() => { + sandbox = sinon.createSandbox() + + // Mock LanguageClient + mockClient = { + sendRequest: sandbox.stub().resolves(), + sendNotification: sandbox.stub(), + } + + // Create logger stub + loggerStub = { + debug: sandbox.stub(), + } + + // Clear all relevant module caches + const configModuleId = require.resolve('../../../../src/lsp/config') + const sharedModuleId = require.resolve('aws-core-vscode/shared') + delete require.cache[configModuleId] + delete require.cache[sharedModuleId] + + // jscpd:ignore-start + // Create getLogger stub and store reference for test verification + getLoggerStub = sandbox.stub().returns(loggerStub) + + // Create a mock shared module with stubbed getLogger + const mockSharedModule = { + getLogger: getLoggerStub, + } + + // Override the require cache with our mock + require.cache[sharedModuleId] = { + id: sharedModuleId, + filename: sharedModuleId, + loaded: true, + parent: undefined, + children: [], + exports: mockSharedModule, + paths: [], + } as any + + // Now require the module - it should use our mocked getLogger + // jscpd:ignore-end + const configModule = require('../../../../src/lsp/config') + pushConfigUpdate = configModule.pushConfigUpdate + }) + + afterEach(() => { + sandbox.restore() + }) + + it('should send profile configuration with logging', async () => { + const config = { + type: 'profile' as const, + profileArn: 'test-profile-arn', + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging + assert(loggerStub.debug.calledWith('Pushing profile configuration: test-profile-arn')) + assert(loggerStub.debug.calledWith('Profile configuration pushed successfully')) + + // Verify client call + assert(mockClient.sendRequest.calledOnce) + assert( + mockClient.sendRequest.calledWith(sinon.match.string, { + section: 'aws.q', + settings: { profileArn: 'test-profile-arn' }, + }) + ) + }) + + it('should send customization configuration with logging', async () => { + const config = { + type: 'customization' as const, + customization: 'test-customization-arn', + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging + assert(loggerStub.debug.calledWith('Pushing customization configuration: test-customization-arn')) + assert(loggerStub.debug.calledWith('Customization configuration pushed successfully')) + + // Verify client call + assert(mockClient.sendNotification.calledOnce) + assert( + mockClient.sendNotification.calledWith(sinon.match.string, { + section: 'aws.q', + settings: { customization: 'test-customization-arn' }, + }) + ) + }) + + it('should handle undefined profile ARN', async () => { + const config = { + type: 'profile' as const, + profileArn: undefined, + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging with undefined + assert(loggerStub.debug.calledWith('Pushing profile configuration: undefined')) + assert(loggerStub.debug.calledWith('Profile configuration pushed successfully')) + }) + + it('should handle undefined customization ARN', async () => { + const config = { + type: 'customization' as const, + customization: undefined, + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging with undefined + assert(loggerStub.debug.calledWith('Pushing customization configuration: undefined')) + assert(loggerStub.debug.calledWith('Customization configuration pushed successfully')) + }) + + it('should send logLevel configuration with logging', async () => { + const config = { + type: 'logLevel' as const, + } + + await pushConfigUpdate(mockClient, config) + + // Verify logging + assert(loggerStub.debug.calledWith('Pushing log level configuration')) + assert(loggerStub.debug.calledWith('Log level configuration pushed successfully')) + + // Verify client call + assert(mockClient.sendNotification.calledOnce) + assert( + mockClient.sendNotification.calledWith(sinon.match.string, { + section: 'aws.logLevel', + }) + ) + }) +}) diff --git a/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts b/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts index 3160f69fa95..8a625fe3544 100644 --- a/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts +++ b/packages/amazonq/test/unit/app/inline/EditRendering/imageRenderer.test.ts @@ -52,6 +52,7 @@ describe('showEdits', function () { delete require.cache[moduleId] delete require.cache[sharedModuleId] + // jscpd:ignore-start // Create getLogger stub and store reference for test verification getLoggerStub = sandbox.stub().returns(loggerStub) @@ -72,6 +73,7 @@ describe('showEdits', function () { } as any // Now require the module - it should use our mocked getLogger + // jscpd:ignore-end const imageRendererModule = require('../../../../../src/app/inline/EditRendering/imageRenderer') showEdits = imageRendererModule.showEdits diff --git a/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts b/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts index 9c1bb751a35..7709eed10fe 100644 --- a/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts +++ b/packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts @@ -21,17 +21,41 @@ describe('securityIssueHoverProvider', () => { token = new vscode.CancellationTokenSource() }) - function buildCommandLink(command: string, args: any[], label: string, tooltip: string): string { - return `[$(${command.includes('ignore') ? 'error' : 'comment'}) ${label}](command:${command}?${encodeURIComponent(JSON.stringify(args))} '${tooltip}')` + function buildCommandLink( + command: string, + commandIcon: string, + args: any[], + label: string, + tooltip: string + ): string { + return `[$(${commandIcon}) ${label}](command:${command}?${encodeURIComponent(JSON.stringify(args))} '${tooltip}')` } function buildExpectedContent(issue: any, fileName: string, description: string, severity?: string): string { const severityBadge = severity ? ` ![${severity}](severity-${severity.toLowerCase()}.svg)` : ' ' const commands = [ - buildCommandLink('aws.amazonq.explainIssue', [issue, fileName], 'Explain', 'Explain with Amazon Q'), - buildCommandLink('aws.amazonq.generateFix', [issue, fileName], 'Fix', 'Fix with Amazon Q'), - buildCommandLink('aws.amazonq.security.ignore', [issue, fileName, 'hover'], 'Ignore', 'Ignore Issue'), - buildCommandLink('aws.amazonq.security.ignoreAll', [issue, 'hover'], 'Ignore All', 'Ignore Similar Issues'), + buildCommandLink( + 'aws.amazonq.explainIssue', + 'comment', + [issue, fileName], + 'Explain', + 'Explain with Amazon Q' + ), + buildCommandLink('aws.amazonq.generateFix', 'wrench', [issue, fileName], 'Fix', 'Fix with Amazon Q'), + buildCommandLink( + 'aws.amazonq.security.ignore', + 'error', + [issue, fileName, 'hover'], + 'Ignore', + 'Ignore Issue' + ), + buildCommandLink( + 'aws.amazonq.security.ignoreAll', + 'error', + [issue, 'hover'], + 'Ignore All', + 'Ignore Similar Issues' + ), ] return `## title${severityBadge}\n${description}\n\n${commands.join('\n | ')}\n` } diff --git a/packages/core/package.json b/packages/core/package.json index 6f8d27ef4dc..d446a1bdf41 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -471,8 +471,8 @@ "@aws-sdk/types": "^3.13.1", "@aws/chat-client": "^0.1.4", "@aws/chat-client-ui-types": "^0.1.47", - "@aws/language-server-runtimes": "^0.2.102", - "@aws/language-server-runtimes-types": "^0.1.43", + "@aws/language-server-runtimes": "^0.2.111", + "@aws/language-server-runtimes-types": "^0.1.47", "@cspotcode/source-map-support": "^0.8.1", "@sinonjs/fake-timers": "^10.0.2", "@types/adm-zip": "^0.4.34", diff --git a/packages/core/src/auth/activation.ts b/packages/core/src/auth/activation.ts index 5c48124c468..8305610dff7 100644 --- a/packages/core/src/auth/activation.ts +++ b/packages/core/src/auth/activation.ts @@ -12,7 +12,7 @@ import { isAmazonQ, isSageMaker } from '../shared/extensionUtilities' import { getLogger } from '../shared/logger/logger' import { getErrorMsg } from '../shared/errors' -interface SagemakerCookie { +export interface SagemakerCookie { authMode?: 'Sso' | 'Iam' } diff --git a/packages/core/src/codewhisperer/commands/basicCommands.ts b/packages/core/src/codewhisperer/commands/basicCommands.ts index 745fe1a45a9..efe993356bd 100644 --- a/packages/core/src/codewhisperer/commands/basicCommands.ts +++ b/packages/core/src/codewhisperer/commands/basicCommands.ts @@ -634,6 +634,12 @@ const registerToolkitApiCallbackOnce = once(() => { export const registerToolkitApiCallback = Commands.declare( { id: 'aws.amazonq.refreshConnectionCallback' }, () => async (toolkitApi?: any) => { + // Early return if already registered to avoid duplicate work + if (_toolkitApi) { + getLogger().debug('Toolkit API callback already registered, skipping') + return + } + // While the Q/CW exposes an API for the Toolkit to register callbacks on auth changes, // we need to do it manually here because the Toolkit would have been unable to call // this API if the Q/CW extension started afterwards (and this code block is running). diff --git a/packages/core/src/codewhisperer/region/regionProfileManager.ts b/packages/core/src/codewhisperer/region/regionProfileManager.ts index e463321be19..24d58d7f588 100644 --- a/packages/core/src/codewhisperer/region/regionProfileManager.ts +++ b/packages/core/src/codewhisperer/region/regionProfileManager.ts @@ -69,7 +69,7 @@ export class RegionProfileManager { constructor(private readonly profileProvider: () => Promise) { super( 'aws.amazonq.regionProfiles.cache', - 60000, + 3600000, { resource: { locked: false, @@ -77,7 +77,7 @@ export class RegionProfileManager { result: undefined, }, }, - { timeout: 15000, interval: 1500, truthy: true } + { timeout: 15000, interval: 500, truthy: true } ) } diff --git a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts index c907f99abe3..bb9fe2cafa4 100644 --- a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts +++ b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts @@ -90,7 +90,7 @@ export class SecurityIssueHoverProvider implements vscode.HoverProvider { const generateFixCommand = this._getCommandMarkdown( 'aws.amazonq.generateFix', [issue, filePath], - 'comment', + 'wrench', 'Fix', 'Fix with Amazon Q' ) diff --git a/packages/core/src/shared/featureConfig.ts b/packages/core/src/shared/featureConfig.ts index d7acb9657be..c7b111b3243 100644 --- a/packages/core/src/shared/featureConfig.ts +++ b/packages/core/src/shared/featureConfig.ts @@ -55,6 +55,9 @@ export const featureDefinitions = new Map([ export class FeatureConfigProvider { private featureConfigs = new Map() + private fetchPromise: Promise | undefined = undefined + private lastFetchTime = 0 + private readonly minFetchInterval = 5000 // 5 seconds minimum between fetches static #instance: FeatureConfigProvider @@ -123,6 +126,28 @@ export class FeatureConfigProvider { return } + // Debounce multiple concurrent calls + const now = performance.now() + if (this.fetchPromise && now - this.lastFetchTime < this.minFetchInterval) { + getLogger().debug('amazonq: Debouncing feature config fetch') + return this.fetchPromise + } + + if (this.fetchPromise) { + return this.fetchPromise + } + + this.lastFetchTime = now + this.fetchPromise = this._fetchFeatureConfigsInternal() + + try { + await this.fetchPromise + } finally { + this.fetchPromise = undefined + } + } + + private async _fetchFeatureConfigsInternal(): Promise { getLogger().debug('amazonq: Fetching feature configs') try { const response = await this.listFeatureEvaluations() diff --git a/packages/core/src/shared/utilities/index.ts b/packages/core/src/shared/utilities/index.ts index ecf753090ca..18d86da4d55 100644 --- a/packages/core/src/shared/utilities/index.ts +++ b/packages/core/src/shared/utilities/index.ts @@ -7,3 +7,4 @@ export { isExtensionInstalled, isExtensionActive } from './vsCodeUtils' export { VSCODE_EXTENSION_ID } from '../extensions' export * from './functionUtils' export * as messageUtils from './messages' +export * as CommentUtils from './commentUtils' diff --git a/packages/core/src/shared/utilities/resourceCache.ts b/packages/core/src/shared/utilities/resourceCache.ts index c0beee61cd6..a399dea66ca 100644 --- a/packages/core/src/shared/utilities/resourceCache.ts +++ b/packages/core/src/shared/utilities/resourceCache.ts @@ -60,6 +60,21 @@ export abstract class CachedResource { abstract resourceProvider(): Promise async getResource(): Promise { + // Check cache without locking first + const quickCheck = this.readCacheOrDefault() + if (quickCheck.resource.result && !quickCheck.resource.locked) { + const duration = now() - quickCheck.resource.timestamp + if (duration < this.expirationInMilli) { + logger.debug( + `cache hit (fast path), duration(%sms) is less than expiration(%sms), returning cached value: %s`, + duration, + this.expirationInMilli, + this.key + ) + return quickCheck.resource.result + } + } + const cachedValue = await this.tryLoadResourceAndLock() const resource = cachedValue?.resource diff --git a/packages/core/src/test/auth/activation.test.ts b/packages/core/src/test/auth/activation.test.ts new file mode 100644 index 00000000000..f203033acba --- /dev/null +++ b/packages/core/src/test/auth/activation.test.ts @@ -0,0 +1,146 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as vscode from 'vscode' +import * as sinon from 'sinon' +import assert from 'assert' +import { initialize, SagemakerCookie } from '../../auth/activation' +import { LoginManager } from '../../auth/deprecated/loginManager' +import * as extensionUtilities from '../../shared/extensionUtilities' +import * as authUtils from '../../auth/utils' +import * as errors from '../../shared/errors' + +describe('auth/activation', function () { + let sandbox: sinon.SinonSandbox + let mockLoginManager: LoginManager + let executeCommandStub: sinon.SinonStub + let isAmazonQStub: sinon.SinonStub + let isSageMakerStub: sinon.SinonStub + let initializeCredentialsProviderManagerStub: sinon.SinonStub + let getErrorMsgStub: sinon.SinonStub + let mockLogger: any + + beforeEach(function () { + sandbox = sinon.createSandbox() + + // Create mocks + mockLoginManager = { + login: sandbox.stub(), + logout: sandbox.stub(), + } as any + + mockLogger = { + warn: sandbox.stub(), + info: sandbox.stub(), + error: sandbox.stub(), + debug: sandbox.stub(), + } + + // Stub external dependencies + executeCommandStub = sandbox.stub(vscode.commands, 'executeCommand') + isAmazonQStub = sandbox.stub(extensionUtilities, 'isAmazonQ') + isSageMakerStub = sandbox.stub(extensionUtilities, 'isSageMaker') + initializeCredentialsProviderManagerStub = sandbox.stub(authUtils, 'initializeCredentialsProviderManager') + getErrorMsgStub = sandbox.stub(errors, 'getErrorMsg') + }) + + afterEach(function () { + sandbox.restore() + }) + + describe('initialize', function () { + it('should not execute sagemaker.parseCookies when not in AmazonQ and SageMaker environment', async function () { + isAmazonQStub.returns(false) + isSageMakerStub.returns(false) + + await initialize(mockLoginManager) + + assert.ok(!executeCommandStub.called) + assert.ok(!initializeCredentialsProviderManagerStub.called) + }) + + it('should not execute sagemaker.parseCookies when only in AmazonQ environment', async function () { + isAmazonQStub.returns(true) + isSageMakerStub.returns(false) + + await initialize(mockLoginManager) + + assert.ok(!executeCommandStub.called) + assert.ok(!initializeCredentialsProviderManagerStub.called) + }) + + it('should not execute sagemaker.parseCookies when only in SageMaker environment', async function () { + isAmazonQStub.returns(false) + isSageMakerStub.returns(true) + + await initialize(mockLoginManager) + + assert.ok(!executeCommandStub.called) + assert.ok(!initializeCredentialsProviderManagerStub.called) + }) + + it('should execute sagemaker.parseCookies when in both AmazonQ and SageMaker environment', async function () { + isAmazonQStub.returns(true) + isSageMakerStub.returns(true) + executeCommandStub.withArgs('sagemaker.parseCookies').resolves({ authMode: 'Sso' } as SagemakerCookie) + + await initialize(mockLoginManager) + + assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) + assert.ok(!initializeCredentialsProviderManagerStub.called) + }) + + it('should initialize credentials provider manager when authMode is not Sso', async function () { + isAmazonQStub.returns(true) + isSageMakerStub.returns(true) + executeCommandStub.withArgs('sagemaker.parseCookies').resolves({ authMode: 'Iam' } as SagemakerCookie) + + await initialize(mockLoginManager) + + assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) + assert.ok(initializeCredentialsProviderManagerStub.calledOnce) + }) + + it('should initialize credentials provider manager when authMode is undefined', async function () { + isAmazonQStub.returns(true) + isSageMakerStub.returns(true) + executeCommandStub.withArgs('sagemaker.parseCookies').resolves({} as SagemakerCookie) + + await initialize(mockLoginManager) + + assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) + assert.ok(initializeCredentialsProviderManagerStub.calledOnce) + }) + + it('should warn and not throw when sagemaker.parseCookies command is not found', async function () { + isAmazonQStub.returns(true) + isSageMakerStub.returns(true) + const error = new Error("command 'sagemaker.parseCookies' not found") + executeCommandStub.withArgs('sagemaker.parseCookies').rejects(error) + getErrorMsgStub.returns("command 'sagemaker.parseCookies' not found") + + await initialize(mockLoginManager) + + assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) + assert.ok(getErrorMsgStub.calledOnceWith(error)) + assert.ok(!initializeCredentialsProviderManagerStub.called) + }) + + it('should throw when sagemaker.parseCookies fails with non-command-not-found error', async function () { + isAmazonQStub.returns(true) + isSageMakerStub.returns(true) + const error = new Error('Some other error') + executeCommandStub.withArgs('sagemaker.parseCookies').rejects(error) + getErrorMsgStub.returns('Some other error') + + await assert.rejects(initialize(mockLoginManager), /Some other error/) + + assert.ok(executeCommandStub.calledOnceWith('sagemaker.parseCookies')) + assert.ok(getErrorMsgStub.calledOnceWith(error)) + assert.ok(!mockLogger.warn.called) + assert.ok(!initializeCredentialsProviderManagerStub.called) + }) + }) +}) diff --git a/packages/core/src/test/lambda/utils.test.ts b/packages/core/src/test/lambda/utils.test.ts index 975738edeba..a3eebe043a7 100644 --- a/packages/core/src/test/lambda/utils.test.ts +++ b/packages/core/src/test/lambda/utils.test.ts @@ -13,6 +13,7 @@ import { setFunctionInfo, compareCodeSha, } from '../../lambda/utils' +import { LambdaFunction } from '../../lambda/commands/uploadLambda' import { DefaultLambdaClient } from '../../shared/clients/lambdaClient' import { fs } from '../../shared/fs/fs' import { tempDirPath } from '../../shared/filesystemUtilities' @@ -116,9 +117,21 @@ describe('lambda utils', function () { }) describe('setFunctionInfo', function () { + let mockLambda: LambdaFunction + + // jscpd:ignore-start + beforeEach(function () { + mockLambda = { + name: 'test-function', + region: 'us-east-1', + configuration: { FunctionName: 'test-function' }, + } + }) + afterEach(function () { sinon.restore() }) + // jscpd:ignore-end it('merges with existing data', async function () { const existingData = { lastDeployed: 123456, undeployed: true, sha: 'old-sha', handlerFile: 'index.js' } @@ -140,9 +153,21 @@ describe('lambda utils', function () { }) describe('compareCodeSha', function () { + let mockLambda: LambdaFunction + + // jscpd:ignore-start + beforeEach(function () { + mockLambda = { + name: 'test-function', + region: 'us-east-1', + configuration: { FunctionName: 'test-function' }, + } + }) + afterEach(function () { sinon.restore() }) + // jscpd:ignore-end it('returns true when local and remote SHA match', async function () { sinon.stub(fs, 'readFileText').resolves(JSON.stringify({ sha: 'same-sha' })) From ff9d816cf3bcc371d87cdcc559494b1aa05775a6 Mon Sep 17 00:00:00 2001 From: Lei Gao <97199248+leigaol@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:04:09 -0700 Subject: [PATCH 43/69] fix(amazonq): correctly update the on show inlay hints for code reference and imports (#7709) ## Problem After Flare migration, the code references and imports are not properly render on suggestion being shown. ## Solution correctly update the on show inlay hints for code reference and imports --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/amazonq/src/app/inline/completion.ts | 2 +- .../amazonq/src/app/inline/sessionManager.ts | 68 ++++++++++++++++++- packages/amazonq/src/lsp/client.ts | 2 + .../amazonq/apps/inline/completion.test.ts | 1 + 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/packages/amazonq/src/app/inline/completion.ts b/packages/amazonq/src/app/inline/completion.ts index 9020deac824..1815b74d570 100644 --- a/packages/amazonq/src/app/inline/completion.ts +++ b/packages/amazonq/src/app/inline/completion.ts @@ -434,7 +434,6 @@ ${itemLog} } item.range = new Range(cursorPosition, cursorPosition) itemsMatchingTypeahead.push(item) - ImportAdderProvider.instance.onShowRecommendation(document, cursorPosition.line, item) } } @@ -458,6 +457,7 @@ ${itemLog} return [] } + this.sessionManager.updateCodeReferenceAndImports() // suggestions returned here will be displayed on screen return itemsMatchingTypeahead as InlineCompletionItem[] } catch (e) { diff --git a/packages/amazonq/src/app/inline/sessionManager.ts b/packages/amazonq/src/app/inline/sessionManager.ts index 3592461ea8c..eaa6eaa23b9 100644 --- a/packages/amazonq/src/app/inline/sessionManager.ts +++ b/packages/amazonq/src/app/inline/sessionManager.ts @@ -4,7 +4,12 @@ */ import * as vscode from 'vscode' import { InlineCompletionItemWithReferences } from '@aws/language-server-runtimes-types' -import { FileDiagnostic, getDiagnosticsOfCurrentFile } from 'aws-core-vscode/codewhisperer' +import { + FileDiagnostic, + getDiagnosticsOfCurrentFile, + ImportAdderProvider, + ReferenceInlineProvider, +} from 'aws-core-vscode/codewhisperer' // TODO: add more needed data to the session interface export interface CodeWhispererSession { @@ -25,7 +30,7 @@ export class SessionManager { private activeSession?: CodeWhispererSession private _acceptedSuggestionCount: number = 0 private _refreshedSessions = new Set() - + private _currentSuggestionIndex = 0 constructor() {} public startSession( @@ -45,6 +50,7 @@ export class SessionManager { firstCompletionDisplayLatency, diagnosticsBeforeAccept, } + this._currentSuggestionIndex = 0 } public closeSession() { @@ -86,6 +92,8 @@ export class SessionManager { public clear() { this.activeSession = undefined + this._currentSuggestionIndex = 0 + this.clearReferenceInlineHintsAndImportHints() } // re-render the session ghost text to display paginated responses once per completed session @@ -103,4 +111,60 @@ export class SessionManager { this._refreshedSessions.add(this.activeSession.sessionId) } } + + public onNextSuggestion() { + if (this.activeSession?.suggestions && this.activeSession?.suggestions.length > 0) { + this._currentSuggestionIndex = (this._currentSuggestionIndex + 1) % this.activeSession.suggestions.length + this.updateCodeReferenceAndImports() + } + } + + public onPrevSuggestion() { + if (this.activeSession?.suggestions && this.activeSession.suggestions.length > 0) { + this._currentSuggestionIndex = + (this._currentSuggestionIndex - 1 + this.activeSession.suggestions.length) % + this.activeSession.suggestions.length + this.updateCodeReferenceAndImports() + } + } + + private clearReferenceInlineHintsAndImportHints() { + ReferenceInlineProvider.instance.removeInlineReference() + ImportAdderProvider.instance.clear() + } + + // Ideally use this API handleDidShowCompletionItem + // https://github.com/microsoft/vscode/blob/main/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts#L83 + updateCodeReferenceAndImports() { + try { + this.clearReferenceInlineHintsAndImportHints() + if ( + this.activeSession?.suggestions && + this.activeSession.suggestions[this._currentSuggestionIndex] && + this.activeSession.suggestions.length > 0 + ) { + const reference = this.activeSession.suggestions[this._currentSuggestionIndex].references + const insertText = this.activeSession.suggestions[this._currentSuggestionIndex].insertText + if (reference && reference.length > 0) { + const insertTextStr = + typeof insertText === 'string' ? insertText : (insertText.value ?? String(insertText)) + + ReferenceInlineProvider.instance.setInlineReference( + this.activeSession.startPosition.line, + insertTextStr, + reference + ) + } + if (vscode.window.activeTextEditor) { + ImportAdderProvider.instance.onShowRecommendation( + vscode.window.activeTextEditor.document, + this.activeSession.startPosition.line, + this.activeSession.suggestions[this._currentSuggestionIndex] + ) + } + } + } catch { + // do nothing as this is not critical path + } + } } diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index e94842123ac..2d8fdb182fc 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -355,10 +355,12 @@ async function onLanguageServerReady( Commands.register('aws.amazonq.showPrev', async () => { await sessionManager.maybeRefreshSessionUx() await vscode.commands.executeCommand('editor.action.inlineSuggest.showPrevious') + sessionManager.onPrevSuggestion() }), Commands.register('aws.amazonq.showNext', async () => { await sessionManager.maybeRefreshSessionUx() await vscode.commands.executeCommand('editor.action.inlineSuggest.showNext') + sessionManager.onNextSuggestion() }), Commands.register({ id: 'aws.amazonq.invokeInlineCompletion', autoconnect: true }, async () => { await vscode.commands.executeCommand('editor.action.inlineSuggest.trigger') diff --git a/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts b/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts index 6bd389046ef..7b079eaad17 100644 --- a/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts +++ b/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts @@ -256,6 +256,7 @@ describe('InlineCompletionManager', () => { getActiveSession: getActiveSessionStub, getActiveRecommendation: getActiveRecommendationStub, clear: () => {}, + updateCodeReferenceAndImports: () => {}, } as unknown as SessionManager getActiveSessionStub.returns({ From 2fb092660732a8c24a4da94fdfa5482fa8b5f3f7 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Tue, 22 Jul 2025 12:11:28 -0700 Subject: [PATCH 44/69] telemetry(lambda): nit to use sessionDuration correctly for debug duration (#7728) ## Problem This change has no customer facing impact. duration -> sessionDuration (correct) Previously this metrics is wrongly recorded to duration and got overwritten by the metrics wrapper ## Solution Change to use sessionDuration correctly --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/core/src/lambda/remoteDebugging/ldkController.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/lambda/remoteDebugging/ldkController.ts b/packages/core/src/lambda/remoteDebugging/ldkController.ts index c4c08b10254..55a777fdc3d 100644 --- a/packages/core/src/lambda/remoteDebugging/ldkController.ts +++ b/packages/core/src/lambda/remoteDebugging/ldkController.ts @@ -729,7 +729,8 @@ export class RemoteDebugController { ) return } - span.record({ duration: this.lastDebugStartTime === 0 ? 0 : Date.now() - this.lastDebugStartTime }) + // use sessionDuration to record debug duration + span.record({ sessionDuration: this.lastDebugStartTime === 0 ? 0 : Date.now() - this.lastDebugStartTime }) try { await vscode.window.withProgress( { From 4a3f0e0f6203cca60b9c50b5a896753cbd6c92ab Mon Sep 17 00:00:00 2001 From: abhraina-aws Date: Tue, 22 Jul 2025 15:29:34 -0700 Subject: [PATCH 45/69] feat(amazonq): enable show logs (#7733) ## Problem We needed a solution to 1 click access the logs on the disk. ## Solution Implemented a button for it. And also wired the related language server changes for this. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/amazonq/src/extension.ts | 10 +- packages/amazonq/src/lsp/chat/messages.ts | 41 ++- packages/amazonq/src/lsp/client.ts | 29 +-- .../amazonq/src/lsp/rotatingLogChannel.ts | 246 ------------------ .../src/test/rotatingLogChannel.test.ts | 192 -------------- 5 files changed, 50 insertions(+), 468 deletions(-) delete mode 100644 packages/amazonq/src/lsp/rotatingLogChannel.ts delete mode 100644 packages/amazonq/src/test/rotatingLogChannel.test.ts diff --git a/packages/amazonq/src/extension.ts b/packages/amazonq/src/extension.ts index 1e26724ff61..53d7cd88037 100644 --- a/packages/amazonq/src/extension.ts +++ b/packages/amazonq/src/extension.ts @@ -45,7 +45,6 @@ import { registerCommands } from './commands' import { focusAmazonQPanel } from 'aws-core-vscode/codewhispererChat' import { activate as activateAmazonqLsp } from './lsp/activation' import { hasGlibcPatch } from './lsp/client' -import { RotatingLogChannel } from './lsp/rotatingLogChannel' export const amazonQContextPrefix = 'amazonq' @@ -104,12 +103,7 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is globals.manifestPaths.endpoints = context.asAbsolutePath(join('resources', 'endpoints.json')) globals.regionProvider = RegionProvider.fromEndpointsProvider(makeEndpointsProvider()) - // Create rotating log channel for all Amazon Q logs - const qLogChannel = new RotatingLogChannel( - 'Amazon Q Logs', - context, - vscode.window.createOutputChannel('Amazon Q Logs', { log: true }) - ) + const qLogChannel = vscode.window.createOutputChannel('Amazon Q Logs', { log: true }) await activateLogger(context, amazonQContextPrefix, qLogChannel) globals.logOutputChannel = qLogChannel globals.loginManager = new LoginManager(globals.awsContext, new CredentialsStore()) @@ -118,8 +112,6 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is getLogger().error('fs.init: invalid env vars found: %O', homeDirLogs) } - getLogger().info('Rotating logger has been setup') - await activateTelemetry(context, globals.awsContext, Settings.instance, 'Amazon Q For VS Code') await initializeAuth(globals.loginManager) diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index f869bbe0da3..38ed7c95c94 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -81,7 +81,14 @@ import { SecurityIssueTreeViewProvider, CodeWhispererConstants, } from 'aws-core-vscode/codewhisperer' -import { amazonQDiffScheme, AmazonQPromptSettings, messages, openUrl, isTextEditor } from 'aws-core-vscode/shared' +import { + amazonQDiffScheme, + AmazonQPromptSettings, + messages, + openUrl, + isTextEditor, + globals, +} from 'aws-core-vscode/shared' import { DefaultAmazonQAppInitContext, messageDispatcher, @@ -429,6 +436,38 @@ export function registerMessageListeners( case listMcpServersRequestType.method: case mcpServerClickRequestType.method: case tabBarActionRequestType.method: + // handling for show_logs button + if (message.params.action === 'show_logs') { + languageClient.info('[VSCode Client] Received show_logs action, showing disclaimer') + + // Show warning message without buttons - just informational + void vscode.window.showWarningMessage( + 'Log files may contain sensitive information such as account IDs, resource names, and other data. Be careful when sharing these logs.' + ) + + // Get the log directory path + const logPath = globals.context.logUri?.fsPath + const result = { ...message.params, success: false } + + if (logPath) { + // Open the log directory in the OS file explorer directly + languageClient.info('[VSCode Client] Opening logs directory') + await vscode.commands.executeCommand('revealFileInOS', vscode.Uri.file(logPath)) + result.success = true + } else { + // Fallback: show error if log path is not available + void vscode.window.showErrorMessage('Log location not available.') + languageClient.error('[VSCode Client] Log location not available') + } + + void webview?.postMessage({ + command: message.command, + params: result, + }) + + break + } + // eslint-disable-next-line no-fallthrough case listAvailableModelsRequestType.method: await resolveChatResponse(message.command, message.params, languageClient, webview) break diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index 2d8fdb182fc..70bb746b456 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -8,7 +8,6 @@ import * as nls from 'vscode-nls' import { LanguageClient, LanguageClientOptions, RequestType, State } from 'vscode-languageclient' import { InlineCompletionManager } from '../app/inline/completion' import { AmazonQLspAuth, encryptionKey, notificationTypes } from './auth' -import { RotatingLogChannel } from './rotatingLogChannel' import { CreateFilesParams, DeleteFilesParams, @@ -95,23 +94,6 @@ export async function startLanguageServer( const clientId = 'amazonq' const traceServerEnabled = Settings.instance.isSet(`${clientId}.trace.server`) - - // Create custom output channel that writes to disk but sends UI output to the appropriate channel - const lspLogChannel = new RotatingLogChannel( - traceServerEnabled ? 'Amazon Q Language Server' : 'Amazon Q Logs', - extensionContext, - traceServerEnabled - ? vscode.window.createOutputChannel('Amazon Q Language Server', { log: true }) - : globals.logOutputChannel - ) - - // Add cleanup for our file output channel - toDispose.push({ - dispose: () => { - lspLogChannel.dispose() - }, - }) - let executable: string[] = [] // apply the GLIBC 2.28 path to node js runtime binary if (isSageMaker()) { @@ -191,6 +173,7 @@ export async function startLanguageServer( window: { notifications: true, showSaveFileDialog: true, + showLogs: true, }, textDocument: { inlineCompletionWithReferences: { @@ -209,9 +192,15 @@ export async function startLanguageServer( }, }, /** - * Using our RotatingLogger for all logs + * When the trace server is enabled it outputs a ton of log messages so: + * When trace server is enabled, logs go to a seperate "Amazon Q Language Server" output. + * Otherwise, logs go to the regular "Amazon Q Logs" channel. */ - outputChannel: lspLogChannel, + ...(traceServerEnabled + ? {} + : { + outputChannel: globals.logOutputChannel, + }), } const client = new LanguageClient( diff --git a/packages/amazonq/src/lsp/rotatingLogChannel.ts b/packages/amazonq/src/lsp/rotatingLogChannel.ts deleted file mode 100644 index b8e3df276f9..00000000000 --- a/packages/amazonq/src/lsp/rotatingLogChannel.ts +++ /dev/null @@ -1,246 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import * as path from 'path' -import * as fs from 'fs' // eslint-disable-line no-restricted-imports -import { getLogger } from 'aws-core-vscode/shared' - -export class RotatingLogChannel implements vscode.LogOutputChannel { - private fileStream: fs.WriteStream | undefined - private originalChannel: vscode.LogOutputChannel - private logger = getLogger('amazonqLsp') - private currentFileSize = 0 - // eslint-disable-next-line @typescript-eslint/naming-convention - private readonly MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB - // eslint-disable-next-line @typescript-eslint/naming-convention - private readonly MAX_LOG_FILES = 4 - private static currentLogPath: string | undefined - - private static generateNewLogPath(logDir: string): string { - const timestamp = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '-').replace('Z', '') - return path.join(logDir, `amazonq-lsp-${timestamp}.log`) - } - - constructor( - public readonly name: string, - private readonly extensionContext: vscode.ExtensionContext, - outputChannel: vscode.LogOutputChannel - ) { - this.originalChannel = outputChannel - this.initFileStream() - } - - private async cleanupOldLogs(): Promise { - try { - const logDir = this.extensionContext.storageUri?.fsPath - if (!logDir) { - return - } - - // Get all log files - const files = await fs.promises.readdir(logDir) - const logFiles = files - .filter((f) => f.startsWith('amazonq-lsp-') && f.endsWith('.log')) - .map((f) => ({ - name: f, - path: path.join(logDir, f), - time: fs.statSync(path.join(logDir, f)).mtime.getTime(), - })) - .sort((a, b) => b.time - a.time) // Sort newest to oldest - - // Remove all but the most recent MAX_LOG_FILES files - for (const file of logFiles.slice(this.MAX_LOG_FILES - 1)) { - try { - await fs.promises.unlink(file.path) - this.logger.debug(`Removed old log file: ${file.path}`) - } catch (err) { - this.logger.error(`Failed to remove old log file ${file.path}: ${err}`) - } - } - } catch (err) { - this.logger.error(`Failed to cleanup old logs: ${err}`) - } - } - - private getLogFilePath(): string { - // If we already have a path, reuse it - if (RotatingLogChannel.currentLogPath) { - return RotatingLogChannel.currentLogPath - } - - const logDir = this.extensionContext.storageUri?.fsPath - if (!logDir) { - throw new Error('No storage URI available') - } - - // Generate initial path - RotatingLogChannel.currentLogPath = RotatingLogChannel.generateNewLogPath(logDir) - return RotatingLogChannel.currentLogPath - } - - private async rotateLog(): Promise { - try { - // Close current stream - if (this.fileStream) { - this.fileStream.end() - } - - const logDir = this.extensionContext.storageUri?.fsPath - if (!logDir) { - throw new Error('No storage URI available') - } - - // Generate new path directly - RotatingLogChannel.currentLogPath = RotatingLogChannel.generateNewLogPath(logDir) - - // Create new log file with new path - this.fileStream = fs.createWriteStream(RotatingLogChannel.currentLogPath, { flags: 'a' }) - this.currentFileSize = 0 - - // Clean up old files - await this.cleanupOldLogs() - - this.logger.info(`Created new log file: ${RotatingLogChannel.currentLogPath}`) - } catch (err) { - this.logger.error(`Failed to rotate log file: ${err}`) - } - } - - private initFileStream() { - try { - const logDir = this.extensionContext.storageUri - if (!logDir) { - this.logger.error('Failed to get storage URI for logs') - return - } - - // Ensure directory exists - if (!fs.existsSync(logDir.fsPath)) { - fs.mkdirSync(logDir.fsPath, { recursive: true }) - } - - const logPath = this.getLogFilePath() - this.fileStream = fs.createWriteStream(logPath, { flags: 'a' }) - this.currentFileSize = 0 - this.logger.info(`Logging to file: ${logPath}`) - } catch (err) { - this.logger.error(`Failed to create log file: ${err}`) - } - } - - get logLevel(): vscode.LogLevel { - return this.originalChannel.logLevel - } - - get onDidChangeLogLevel(): vscode.Event { - return this.originalChannel.onDidChangeLogLevel - } - - trace(message: string, ...args: any[]): void { - this.originalChannel.trace(message, ...args) - this.writeToFile(`[TRACE] ${message}`) - } - - debug(message: string, ...args: any[]): void { - this.originalChannel.debug(message, ...args) - this.writeToFile(`[DEBUG] ${message}`) - } - - info(message: string, ...args: any[]): void { - this.originalChannel.info(message, ...args) - this.writeToFile(`[INFO] ${message}`) - } - - warn(message: string, ...args: any[]): void { - this.originalChannel.warn(message, ...args) - this.writeToFile(`[WARN] ${message}`) - } - - error(message: string | Error, ...args: any[]): void { - this.originalChannel.error(message, ...args) - this.writeToFile(`[ERROR] ${message instanceof Error ? message.stack || message.message : message}`) - } - - append(value: string): void { - this.originalChannel.append(value) - this.writeToFile(value) - } - - appendLine(value: string): void { - this.originalChannel.appendLine(value) - this.writeToFile(value + '\n') - } - - replace(value: string): void { - this.originalChannel.replace(value) - this.writeToFile(`[REPLACE] ${value}`) - } - - clear(): void { - this.originalChannel.clear() - } - - show(preserveFocus?: boolean): void - show(column?: vscode.ViewColumn, preserveFocus?: boolean): void - show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void { - if (typeof columnOrPreserveFocus === 'boolean') { - this.originalChannel.show(columnOrPreserveFocus) - } else { - this.originalChannel.show(columnOrPreserveFocus, preserveFocus) - } - } - - hide(): void { - this.originalChannel.hide() - } - - dispose(): void { - // First dispose the original channel - this.originalChannel.dispose() - - // Close our file stream if it exists - if (this.fileStream) { - this.fileStream.end() - } - - // Clean up all log files - const logDir = this.extensionContext.storageUri?.fsPath - if (logDir) { - try { - const files = fs.readdirSync(logDir) - for (const file of files) { - if (file.startsWith('amazonq-lsp-') && file.endsWith('.log')) { - fs.unlinkSync(path.join(logDir, file)) - } - } - this.logger.info('Cleaned up all log files during disposal') - } catch (err) { - this.logger.error(`Failed to cleanup log files during disposal: ${err}`) - } - } - } - - private writeToFile(content: string): void { - if (this.fileStream) { - try { - const timestamp = new Date().toISOString() - const logLine = `${timestamp} ${content}\n` - const size = Buffer.byteLength(logLine) - - // If this write would exceed max file size, rotate first - if (this.currentFileSize + size > this.MAX_FILE_SIZE) { - void this.rotateLog() - } - - this.fileStream.write(logLine) - this.currentFileSize += size - } catch (err) { - this.logger.error(`Failed to write to log file: ${err}`) - void this.rotateLog() - } - } - } -} diff --git a/packages/amazonq/src/test/rotatingLogChannel.test.ts b/packages/amazonq/src/test/rotatingLogChannel.test.ts deleted file mode 100644 index 87c4c109603..00000000000 --- a/packages/amazonq/src/test/rotatingLogChannel.test.ts +++ /dev/null @@ -1,192 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -// eslint-disable-next-line no-restricted-imports -import * as fs from 'fs' -import * as path from 'path' -import * as assert from 'assert' -import { RotatingLogChannel } from '../lsp/rotatingLogChannel' - -describe('RotatingLogChannel', () => { - let testDir: string - let mockExtensionContext: vscode.ExtensionContext - let mockOutputChannel: vscode.LogOutputChannel - let logChannel: RotatingLogChannel - - beforeEach(() => { - // Create a temp test directory - testDir = fs.mkdtempSync('amazonq-test-logs-') - - // Mock extension context - mockExtensionContext = { - storageUri: { fsPath: testDir } as vscode.Uri, - } as vscode.ExtensionContext - - // Mock output channel - mockOutputChannel = { - name: 'Test Output Channel', - append: () => {}, - appendLine: () => {}, - replace: () => {}, - clear: () => {}, - show: () => {}, - hide: () => {}, - dispose: () => {}, - trace: () => {}, - debug: () => {}, - info: () => {}, - warn: () => {}, - error: () => {}, - logLevel: vscode.LogLevel.Info, - onDidChangeLogLevel: new vscode.EventEmitter().event, - } - - // Create log channel instance - logChannel = new RotatingLogChannel('test', mockExtensionContext, mockOutputChannel) - }) - - afterEach(() => { - // Cleanup test directory - if (fs.existsSync(testDir)) { - fs.rmSync(testDir, { recursive: true, force: true }) - } - }) - - it('creates log file on initialization', () => { - const files = fs.readdirSync(testDir) - assert.strictEqual(files.length, 1) - assert.ok(files[0].startsWith('amazonq-lsp-')) - assert.ok(files[0].endsWith('.log')) - }) - - it('writes logs to file', async () => { - const testMessage = 'test log message' - logChannel.info(testMessage) - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') - assert.ok(content.includes(testMessage)) - }) - - it('rotates files when size limit is reached', async () => { - // Write enough data to trigger rotation - const largeMessage = 'x'.repeat(1024 * 1024) // 1MB - for (let i = 0; i < 6; i++) { - // Should create at least 2 files - logChannel.info(largeMessage) - } - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - assert.ok(files.length > 1, 'Should have created multiple log files') - assert.ok(files.length <= 4, 'Should not exceed max file limit') - }) - - it('keeps only the specified number of files', async () => { - // Write enough data to create more than MAX_LOG_FILES - const largeMessage = 'x'.repeat(1024 * 1024) // 1MB - for (let i = 0; i < 20; i++) { - // Should trigger multiple rotations - logChannel.info(largeMessage) - } - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - assert.strictEqual(files.length, 4, 'Should keep exactly 4 files') - }) - - it('cleans up all files on dispose', async () => { - // Write some logs - logChannel.info('test message') - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - // Verify files exist - assert.ok(fs.readdirSync(testDir).length > 0) - - // Dispose - logChannel.dispose() - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - // Verify files are cleaned up - const remainingFiles = fs.readdirSync(testDir).filter((f) => f.startsWith('amazonq-lsp-') && f.endsWith('.log')) - assert.strictEqual(remainingFiles.length, 0, 'Should have no log files after disposal') - }) - - it('includes timestamps in log messages', async () => { - const testMessage = 'test message' - logChannel.info(testMessage) - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') - - // ISO date format regex - const timestampRegex = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/ - assert.ok(timestampRegex.test(content), 'Log entry should include ISO timestamp') - }) - - it('handles different log levels correctly', async () => { - const testMessage = 'test message' - logChannel.trace(testMessage) - logChannel.debug(testMessage) - logChannel.info(testMessage) - logChannel.warn(testMessage) - logChannel.error(testMessage) - - // Allow async operations to complete - await new Promise((resolve) => setTimeout(resolve, 100)) - - const files = fs.readdirSync(testDir) - const content = fs.readFileSync(path.join(testDir, files[0]), 'utf-8') - - assert.ok(content.includes('[TRACE]'), 'Should include TRACE level') - assert.ok(content.includes('[DEBUG]'), 'Should include DEBUG level') - assert.ok(content.includes('[INFO]'), 'Should include INFO level') - assert.ok(content.includes('[WARN]'), 'Should include WARN level') - assert.ok(content.includes('[ERROR]'), 'Should include ERROR level') - }) - - it('delegates log level to the original channel', () => { - // Set up a mock output channel with a specific log level - const mockChannel = { - ...mockOutputChannel, - logLevel: vscode.LogLevel.Trace, - } - - // Create a new log channel with the mock - const testLogChannel = new RotatingLogChannel('test-delegate', mockExtensionContext, mockChannel) - - // Verify that the log level is delegated correctly - assert.strictEqual( - testLogChannel.logLevel, - vscode.LogLevel.Trace, - 'Should delegate log level to original channel' - ) - - // Change the mock's log level - mockChannel.logLevel = vscode.LogLevel.Debug - - // Verify that the change is reflected - assert.strictEqual( - testLogChannel.logLevel, - vscode.LogLevel.Debug, - 'Should reflect changes to original channel log level' - ) - }) -}) From b980406b8de988011dd8482684bec224d49a01f3 Mon Sep 17 00:00:00 2001 From: Lei Gao <97199248+leigaol@users.noreply.github.com> Date: Wed, 23 Jul 2025 10:01:43 -0700 Subject: [PATCH 46/69] fix(amazonq): fix Inline completion acceptance and reject telemetry race condition (#7734) ## Problem When Q is editing (on accept, on reject is in progress), if we trigger again, the session is not closed yet, global state varaibles are still in progress to be cleared, and we will report wrong user trigger decision telemetry. This is worse for acceptance since it has a await sleep for diagnostics to update. ## Solution Do not let it trigger when Q is editing! This is not customer facing so no change log. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/amazonq/src/app/inline/completion.ts | 147 +++++++++--------- 1 file changed, 73 insertions(+), 74 deletions(-) diff --git a/packages/amazonq/src/app/inline/completion.ts b/packages/amazonq/src/app/inline/completion.ts index 1815b74d570..be49a0654cc 100644 --- a/packages/amazonq/src/app/inline/completion.ts +++ b/packages/amazonq/src/app/inline/completion.ts @@ -34,7 +34,6 @@ import { vsCodeState, inlineCompletionsDebounceDelay, noInlineSuggestionsMsg, - ReferenceInlineProvider, getDiagnosticsDifferences, getDiagnosticsOfCurrentFile, toIdeDiagnostics, @@ -111,88 +110,88 @@ export class InlineCompletionManager implements Disposable { startLine: number, firstCompletionDisplayLatency?: number ) => { - // TODO: also log the seen state for other suggestions in session - // Calculate timing metrics before diagnostic delay - const totalSessionDisplayTime = performance.now() - requestStartTime - await sleep(1000) - const diagnosticDiff = getDiagnosticsDifferences( - this.sessionManager.getActiveSession()?.diagnosticsBeforeAccept, - getDiagnosticsOfCurrentFile() - ) - const params: LogInlineCompletionSessionResultsParams = { - sessionId: sessionId, - completionSessionResult: { - [item.itemId]: { - seen: true, - accepted: true, - discarded: false, - }, - }, - totalSessionDisplayTime: totalSessionDisplayTime, - firstCompletionDisplayLatency: firstCompletionDisplayLatency, - addedDiagnostics: diagnosticDiff.added.map((it) => toIdeDiagnostics(it)), - removedDiagnostics: diagnosticDiff.removed.map((it) => toIdeDiagnostics(it)), - } - this.languageClient.sendNotification(this.logSessionResultMessageName, params) - this.disposable.dispose() - this.disposable = languages.registerInlineCompletionItemProvider( - CodeWhispererConstants.platformLanguageIds, - this.inlineCompletionProvider - ) - if (item.references && item.references.length) { - const referenceLog = ReferenceLogViewProvider.getReferenceLog( - item.insertText as string, - item.references, - editor + try { + vsCodeState.isCodeWhispererEditing = true + // TODO: also log the seen state for other suggestions in session + // Calculate timing metrics before diagnostic delay + const totalSessionDisplayTime = performance.now() - requestStartTime + await sleep(500) + const diagnosticDiff = getDiagnosticsDifferences( + this.sessionManager.getActiveSession()?.diagnosticsBeforeAccept, + getDiagnosticsOfCurrentFile() ) - ReferenceLogViewProvider.instance.addReferenceLog(referenceLog) - ReferenceHoverProvider.instance.addCodeReferences(item.insertText as string, item.references) - - // Show codelense for 5 seconds. - ReferenceInlineProvider.instance.setInlineReference( - startLine, - item.insertText as string, - item.references + const params: LogInlineCompletionSessionResultsParams = { + sessionId: sessionId, + completionSessionResult: { + [item.itemId]: { + seen: true, + accepted: true, + discarded: false, + }, + }, + totalSessionDisplayTime: totalSessionDisplayTime, + firstCompletionDisplayLatency: firstCompletionDisplayLatency, + addedDiagnostics: diagnosticDiff.added.map((it) => toIdeDiagnostics(it)), + removedDiagnostics: diagnosticDiff.removed.map((it) => toIdeDiagnostics(it)), + } + this.languageClient.sendNotification(this.logSessionResultMessageName, params) + this.disposable.dispose() + this.disposable = languages.registerInlineCompletionItemProvider( + CodeWhispererConstants.platformLanguageIds, + this.inlineCompletionProvider ) - setTimeout(() => { - ReferenceInlineProvider.instance.removeInlineReference() - }, 5000) - } - if (item.mostRelevantMissingImports?.length) { - await ImportAdderProvider.instance.onAcceptRecommendation(editor, item, startLine) + if (item.references && item.references.length) { + const referenceLog = ReferenceLogViewProvider.getReferenceLog( + item.insertText as string, + item.references, + editor + ) + ReferenceLogViewProvider.instance.addReferenceLog(referenceLog) + ReferenceHoverProvider.instance.addCodeReferences(item.insertText as string, item.references) + } + if (item.mostRelevantMissingImports?.length) { + await ImportAdderProvider.instance.onAcceptRecommendation(editor, item, startLine) + } + this.sessionManager.incrementSuggestionCount() + // clear session manager states once accepted + this.sessionManager.clear() + } finally { + vsCodeState.isCodeWhispererEditing = false } - this.sessionManager.incrementSuggestionCount() - // clear session manager states once accepted - this.sessionManager.clear() } commands.registerCommand('aws.amazonq.acceptInline', onInlineAcceptance) const onInlineRejection = async () => { - await commands.executeCommand('editor.action.inlineSuggest.hide') - // TODO: also log the seen state for other suggestions in session - this.disposable.dispose() - this.disposable = languages.registerInlineCompletionItemProvider( - CodeWhispererConstants.platformLanguageIds, - this.inlineCompletionProvider - ) - const sessionId = this.sessionManager.getActiveSession()?.sessionId - const itemId = this.sessionManager.getActiveRecommendation()[0]?.itemId - if (!sessionId || !itemId) { - return - } - const params: LogInlineCompletionSessionResultsParams = { - sessionId: sessionId, - completionSessionResult: { - [itemId]: { - seen: true, - accepted: false, - discarded: false, + try { + vsCodeState.isCodeWhispererEditing = true + await commands.executeCommand('editor.action.inlineSuggest.hide') + // TODO: also log the seen state for other suggestions in session + this.disposable.dispose() + this.disposable = languages.registerInlineCompletionItemProvider( + CodeWhispererConstants.platformLanguageIds, + this.inlineCompletionProvider + ) + const sessionId = this.sessionManager.getActiveSession()?.sessionId + const itemId = this.sessionManager.getActiveRecommendation()[0]?.itemId + if (!sessionId || !itemId) { + return + } + const params: LogInlineCompletionSessionResultsParams = { + sessionId: sessionId, + completionSessionResult: { + [itemId]: { + seen: true, + accepted: false, + discarded: false, + }, }, - }, + } + this.languageClient.sendNotification(this.logSessionResultMessageName, params) + // clear session manager states once rejected + this.sessionManager.clear() + } finally { + vsCodeState.isCodeWhispererEditing = false } - this.languageClient.sendNotification(this.logSessionResultMessageName, params) - // clear session manager states once rejected - this.sessionManager.clear() } commands.registerCommand('aws.amazonq.rejectCodeSuggestion', onInlineRejection) } From d0082e651d5a0595f88332f83caf6ce6041de632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=A5=A9=20Flora?= Date: Wed, 23 Jul 2025 11:04:36 -0700 Subject: [PATCH 47/69] other(amazonq): remove unnecessary notes file --- P261194666.md | 630 -------------------------------------------------- 1 file changed, 630 deletions(-) delete mode 100644 P261194666.md diff --git a/P261194666.md b/P261194666.md deleted file mode 100644 index feafb3e7ce2..00000000000 --- a/P261194666.md +++ /dev/null @@ -1,630 +0,0 @@ -# Root-cause SageMaker auth failure in Amazon Q for VSCode after v1.62.0 - -v1.63.0 of the extension introduced agentic chat and moved from directly calling the Q service using an AWS SDK client directly from the extension to indirectly -calling the Q service through the aws-lsp-codewhisperer service in language-servers (aka Flare). - -## Notes - -1. `isSageMaker` function used in many places in aws-toolkit-vscode codebase. `setContext` is used to set whether SMAI (`aws.isSageMaker`) or SMUS (`aws.isSageMakerUnifiedStudio`) is in use so that conditions testing those values can be used in package.json. -2. `aws-toolkit-vscode/packages/amazonq/src/lsp/chat/webviewProvider.ts` passes in booleans for SMAI and SMUS into MynahUI for Q Chat. -3. Files in `aws-toolkit-vscode/packages/core` apply to how the Amazon Q for VSCode extension used to call Q (formerly known as "CodeWhisperer") directly through an SDK client. It was this codebase that SageMaker depended on for authenticating with IAM credentials to Q. Files in `aws-toolkit-vscode/packages/amazonq` apply to how the extension now uses the LSP server (aka aws-lsp-codewhisperer in language-servers aka Flare) for indirectly accessing the Q service. The commit `938bb376647414776a55d7dd7d6761c863764c5c` is primarily what flipped over the extension from using core to amazonq leading to auth breaking for SageMaker. -4. Once we figure out how IAM credentials worked before (likely because core creates it's own SDK client and may do something fancy with auth that aws-lsp-codewhisperer does not), we may find that we need to apply a fix in aws-toolkit-vscode and/or language-servers. -5. Using the core (legacy) Q chat is not an option as the Amazon Q for VSCode team will not be maintaining it. -6. In user settings in VSCode, set `amazonq.trace.server` to `on` for more detailed logs from LSP server. -7. /Users/floralph/Source/P261194666.md contains A LOT of information about researching this issue so far. It can fill your context fast. We will refer to it from time to time and possibly migrate some of the most important information to this doc. You can ask about reading it, but don't read it unless I instruct you to do so and even then, you MUST stay focused only on what you've been asked to do with it. - -## This is CRITICAL - -When trying to root cause the issue, it is ABSOLUTELY CRITICAL that we follow the path of execution related to the CodeWhisperer LSP server from start onwards without dropping the trail. We CANNOT just assume things about other parts of code based on names nor should we assume they are even related to our issue if they are not in the specific code path that we're following. We have to be laser focused on following the code path and looking for issues, not jumping to conclusions and jumping to other code. As we have to use logging as our only means of tracing/debugging in the SageMaker instance, we can use that to follow the path of execution. - -## Repos on disk - -1. /Users/floralph/Source/aws-toolkit-vscode - 1. Branch from breaking commit 938bb376647414776a55d7dd7d6761c863764c5c for experimenting on: bug/sm-auth - 2. Branch where you tried to add IAM creds using /Users/floralph/Source/P261194666.md: floralph/P261194666 -2. /Users/floralph/Source/language-server-runtimes -3. /Users/floralph/Source/language-servers - -If we absolutely need to look at MynahUI code, I can try to track it down. It might be lingering in one of the repos above though. - -## Important files likely related to the issue and fix - -- aws-toolkit-vscode/packages/amazonq/src/extensionNode.ts - -## Git Bisect Results - Breaking Commit - -**Commit ID:** `938bb376647414776a55d7dd7d6761c863764c5c` -**Author:** Josh Pinkney -**Date:** Not specified in bisect output -**Title:** Enable Amazon Q LSP experiments by default - -### What This Commit Changed - -This commit flipped three experiment flags from `false` to `true`, fundamentally changing Amazon Q's architecture from legacy chat system to LSP-based system: - -```diff -diff --git a/packages/amazonq/src/extension.ts b/packages/amazonq/src/extension.ts -index fe5ce809c..345e6e646 100644 ---- a/packages/amazonq/src/extension.ts -+++ b/packages/amazonq/src/extension.ts -@@ -119,7 +119,7 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is - } - // This contains every lsp agnostic things (auth, security scan, code scan) - await activateCodeWhisperer(extContext as ExtContext) -- if (Experiments.instance.get('amazonqLSP', false)) { -+ if (Experiments.instance.get('amazonqLSP', true)) { - await activateAmazonqLsp(context) - } - -diff --git a/packages/amazonq/src/extensionNode.ts b/packages/amazonq/src/extensionNode.ts -index d3e98b025..5a8b5082c 100644 ---- a/packages/amazonq/src/extensionNode.ts -+++ b/packages/amazonq/src/extensionNode.ts -@@ -53,7 +53,7 @@ async function activateAmazonQNode(context: vscode.ExtensionContext) { - extensionContext: context, - } - -- if (!Experiments.instance.get('amazonqChatLSP', false)) { -+ if (!Experiments.instance.get('amazonqChatLSP', true)) { - const appInitContext = DefaultAmazonQAppInitContext.instance - const provider = new AmazonQChatViewProvider( - context, -diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts -index e45a3fdac..12341ff17 100644 ---- a/packages/amazonq/src/lsp/client.ts -+++ b/packages/amazonq/src/lsp/client.ts -@@ -117,7 +117,7 @@ export async function startLanguageServer( - ) - } - -- if (Experiments.instance.get('amazonqChatLSP', false)) { -+ if (Experiments.instance.get('amazonqChatLSP', true)) { - await activate(client, encryptionKey, resourcePaths.ui) - } -``` - -### Commit: 6ce383258 - "feat(sagemaker): free tier Q Chat with auto-login for iam users and login option for pro tier users (#5886)" - -This commit shows how the Amazon Q for VSCode extension was updated (core only as the LSP server was not used by this extension at the time) to use -IAM credentials from SageMaker. - -**Author:** Ahmed Ali (azkali) -**Date:** October 29, 2024 - -#### Key Auth-Related Changes: - -1. **SageMaker Cookie-Based Authentication Detection** in `packages/core/src/auth/activation.ts`: - - ```typescript - interface SagemakerCookie { - authMode?: 'Sso' | 'Iam' - } - - export async function initialize(loginManager: LoginManager): Promise { - if (isAmazonQ() && isSageMaker()) { - // The command `sagemaker.parseCookies` is registered in VS Code Sagemaker environment. - const result = (await vscode.commands.executeCommand('sagemaker.parseCookies')) as SagemakerCookie - if (result.authMode !== 'Sso') { - initializeCredentialsProviderManager() - } - } - ``` - -2. **New Credentials Provider Manager Initialization** in `packages/core/src/auth/utils.ts`: - - ```typescript - export function initializeCredentialsProviderManager() { - const manager = CredentialsProviderManager.getInstance() - manager.addProviderFactory(new SharedCredentialsProviderFactory()) - manager.addProviders( - new Ec2CredentialsProvider(), - new EcsCredentialsProvider(), - new EnvVarsCredentialsProvider() - ) - } - ``` - -3. **Modified CodeWhisperer Auth Validation** in `packages/core/src/codewhisperer/util/authUtil.ts`: - - ```typescript - // BEFORE: - if (isSageMaker()) { - return isIamConnection(conn) - } - - // AFTER: - return ( - (isSageMaker() && isIamConnection(conn)) || - (isCloud9('codecatalyst') && isIamConnection(conn)) || - (isSsoConnection(conn) && hasScopes(conn, codeWhispererCoreScopes)) - ) - ``` - -4. **Amazon Q Connection Validation Enhanced**: - - ```typescript - export const isValidAmazonQConnection = (conn?: Connection): conn is Connection => { - return ( - (isSageMaker() && isIamConnection(conn)) || - ((isSsoConnection(conn) || isBuilderIdConnection(conn)) && - isValidCodeWhispererCoreConnection(conn) && - hasScopes(conn, amazonQScopes)) - ) - } - ``` - -5. **Dual Chat Client Implementation** in `packages/core/src/codewhispererChat/clients/chat/v0/chat.ts`: - - ```typescript - // New IAM-based chat method - async chatIam(chatRequest: SendMessageRequest): Promise { - const client = await createQDeveloperStreamingClient() - const response = await client.sendMessage(chatRequest) - // ... session handling - } - - // Existing SSO-based chat method - async chatSso(chatRequest: GenerateAssistantResponseRequest): Promise { - const client = await createCodeWhispererChatStreamingClient() - // ... existing logic - } - ``` - -6. **Chat Controller Route Selection** in `packages/core/src/codewhispererChat/controllers/chat/controller.ts`: - ```typescript - if (isSsoConnection(AuthUtil.instance.conn)) { - const { $metadata, generateAssistantResponseResponse } = await session.chatSso(request) - response = { $metadata: $metadata, message: generateAssistantResponseResponse } - } else { - const { $metadata, sendMessageResponse } = await session.chatIam(request as SendMessageRequest) - response = { $metadata: $metadata, message: sendMessageResponse } - } - ``` - -#### Key Findings: - -- **Two separate Q API clients**: `createQDeveloperStreamingClient()` for IAM, `createCodeWhispererChatStreamingClient()` for SSO -- **SageMaker cookie-based auth detection**: Uses `sagemaker.parseCookies` command to determine auth mode -- **Automatic credential provider setup**: Initializes EC2, ECS, and environment variable credential providers for IAM users -- **Route selection based on connection type**: SSO connections use old client, IAM connections use new Q Developer client - -## Related Historical Fix - CodeWhisperer SageMaker Authentication - -**Commit ID:** `b125a1bd3b135344d2aa24961e746a10e55702c6` -**Author:** Lei Gao -**Date:** March 18, 2024 -**Title:** "fix(codewhisperer): completion error in sagemaker #4545" - -### Problem Identified - -In SageMaker Code Editor, CodeWhisperer was failing with: - -``` -Unexpected key 'optOutPreference' found in params -``` - -### Root Cause - -SageMaker environments require **GenerateRecommendation** API calls instead of **ListRecommendation** API calls for SigV4 authentication to work properly. - -### Fix Applied - -Modified `packages/core/src/codewhisperer/service/recommendationHandler.ts`: - -```typescript -// BEFORE: Used pagination logic that triggered ListRecommendation -if (pagination) { - // ListRecommendation request - FAILS in SageMaker -} - -// AFTER: SageMaker detection forces GenerateRecommendation -if (pagination && !isSM) { - // Added !isSM condition - // ListRecommendation only for non-SageMaker -} else { - // GenerateRecommendation for SageMaker (and non-pagination cases) -} -``` - -## Key Insights - -### Pattern Recognition - -Both issues share the same fundamental problem: **SageMaker environments have different API authentication requirements** that break standard AWS SDK calls. - -### Hypothesis for Current Issue - -The Amazon Q LSP (enabled by default in v1.63.0) is likely making API calls that: - -1. Work fine in standard environments -2. Fail in SageMaker due to different credential passing mechanisms -3. Require SageMaker-specific request formatting (similar to CodeWhisperer fix) - -# Files - -## aws-toolkit-vscode/packages/amazonq/src/extension.ts - -Starts both the old "core" CodeWhisperer code with `await activateCodeWhisperer(extContext as ExtContext)` on line ~121, followed by the new LSP code for Amazon Q. Maybe the dev team is slowly migrating functionality from core to amazonq and this is how they have both running at once. The code below is one of 3 places where the 'amazonqLSP' experiment is set to on by default in the commit that broke SageMaker auth. - -```typescript -// This contains every lsp agnostic things (auth, security scan, code scan) -await activateCodeWhisperer(extContext as ExtContext) -if (Experiments.instance.get('amazonqLSP', true)) { - await activateAmazonqLsp(context) -} -``` - -`activateAmazonqLsp` downloads and installs the language-servers bundle then executes the CodeWhisperer start up script (we should find the specific name and path) and initializes the LSP server, including auth set up. - -## aws-toolkit-vscode/packages/core/src/auth/activation.ts - -This file appears critical to how the SageMaker auth worked. It is in core however, and not clear whether it is even in the code path for the LSP server or not. We should review this file closely to understand how IAM credentials worked as it should inform us on what needs to change in the amazonq package to support IAM credentials as well. The `sagemaker.parseCookies` code here also seems important in determining whether the SageMaker instance wants to use IAM or SSO, so that should probably be carried over into the amazonq package as well. - -The `Auth.instance.onDidChangeActiveConnection` handler code should be investigated further. It's not clear if it has anything to do with auth to Q or if it's just older "toolkit"-related auth stuff. - -## aws-toolkit-vscode/packages/core/src/auth/utils.ts - -This is a collection of utility functions and many are related to auth/security. However, it appears to be `initializeCredentialsProviderManager` in our code path, called by `aws-toolkit-vscode/packages/core/src/auth/activation.ts` that may be of importance. We should determine if we need this or similar functionality in amazonq package or if this is just a hold-over that updates the old "toolkit" (i.e. non-Amazon Q parts of the extension) stuff. - -## aws-toolkit-vscode/packages/amazonq/src/lsp/client.ts - -1. line ~68 sets `providesBearerToken: true` but doesn't appear to have anything similar for IAM credentials. -2. line ~93 to the end starts auth for LSP using the `AmazonQLspAuth` class. This all appears to be for SSO tokens, nothing for IAM credentials. - -## aws-toolkit-vscode/packages/amazonq/src/lsp/auth.ts - -1. Defines `AmazonQLspAuth` class that is only for SSO tokens, nothing about IAM credentials. -2. Some SSO token related functions are exported, but nothing similar for IAM credentials. - -## aws-toolkit-vscode/packages/core/src/codewhisperer/activation.ts - -`activate` in the old "core" Q implementation is called by `aws-toolkit-vscode/packages/amazonq/src/extension.ts` line ~121. - -Suspcious code that is still running in `activate` function. How does this not interfer with the new auth code in the amazonq package? - -```typescript -// initialize AuthUtil earlier to make sure it can listen to connection change events. -const auth = AuthUtil.instance -auth.initCodeWhispererHooks() -``` - -Further down in this file it still creates and uses `onst client = new codewhispererClient.DefaultCodeWhispererClient()` which makes it appear to be using both direct calls from the extension as well as the LSP to access the Q service. This bears further investigation into what this code is actually doing. - -## aws-toolkit-vscode/packages/core/src/codewhisperer/client/codewhisperer.ts - -This is the old "core" CodeWhisperer service client. There is likely important code here that informs how IAM authentication works with the service client that may be missing in the language-servers CodeWhisperer client. If my hunch is correct in that the "core" code is still in use for what hasn't been migrated yet, this code may not be actively used for Q Chat which was migrated (see the Experiments flags defaulting to true in the breaking commit) to the amazonq package and should be using the auth there and in language-servers. - -## aws-toolkit-vscode/packages/amazonq/src/extensionNode.ts - -The code below is one of 3 places where the 'amazonqChatLSP' experiment is set to on by default in the commit that broke SageMaker auth. There is some "auth"-related code in this file that should be investigated further to determine if it has any impact on the broken SageMaker auth. It isn't obvious that it does or doesn't. It may just be used in the MynahUI Q Chat webview, and not the LSP server. - -```typescript -if (!Experiments.instance.get('amazonqChatLSP', true)) { -``` - -## aws-toolkit-vscode/packages/core/src/auth/auth.ts - -This file was updated recently for the SMUS project. It may not be directly related to the broken SageMaker auth issue, but the comments on the added/changed functions are suspicious regarding how credentials are received. SMUS may be adding a different way to get IAM credentials than what SMAI used. - -```typescript -/** - * Returns true if credentials are provided by the environment (ex. via ~/.aws/) - * - * @param isC9 boolean for if Cloud9 is host - * @param isSM boolean for if SageMaker is host - * @returns boolean for if C9 "OR" SM - */ -export function hasVendedIamCredentials(isC9?: boolean, isSM?: boolean) { - isC9 ??= isCloud9() - isSM ??= isSageMaker() - return isSM || isC9 -} - -/** - * Returns true if credentials are provided by the metadata files in environment (ex. for IAM via ~/.aws/ and in a future case with SSO, from /cache or /sso) - * @param isSMUS boolean if SageMaker Unified Studio is host - * @returns boolean if SMUS - */ -export function hasVendedCredentialsFromMetadata(isSMUS?: boolean) { - isSMUS ??= isSageMaker('SMUS') - return isSMUS -} -``` - -There is also A LOT of other auth related functionality here, but it's in "core" and may not be directly related the code paths for LSP and breaking auth in SageMaker. - -## aws-toolkit-vscode/packages/core/src/codewhisperer/util/authUtil.ts - -There is some `isSageMaker`-related code here that we should investigate. It appears to be important to auth with SageMaker, but it's not clear if it or similar code is needed and has made it into the amazonq package. Once we confirm any of this code is in our code path of concern, it should be investigated further. - -## aws-toolkit-vscode/packages/amazonq/src/lsp/chat/webviewProvider.ts - -While there is special SageMaker handling in this file, it is not clear if it is related to IAM auth issues with the LSP or it is just related to the chat UI. If we find it is in our code path, we can investigate further. - -# Proposed Fix for SageMaker IAM Authentication in Amazon Q LSP - -> **NOTE:** We should start back tomorrow by addressing the issues and concerns raised in this document first thing, particularly the SageMaker cookie detection and connection metadata handling for IAM authentication. - -## Issue Summary - -The Amazon Q extension for VSCode fails to authenticate in SageMaker environments after v1.62.0 due to a change in architecture. The extension moved from directly calling the Q service using an AWS SDK client to indirectly calling it through the aws-lsp-codewhisperer service (Flare). While the old implementation had specific handling for SageMaker IAM credentials, the new LSP-based implementation only supports SSO token authentication. - -## Root Cause Analysis - -### Breaking Change - -Commit `938bb376647414776a55d7dd7d6761c863764c5c` enabled three experiment flags by default: - -1. `amazonqLSP` in `packages/amazonq/src/extension.ts` (line ~119) - Controls whether to activate the Amazon Q LSP -2. `amazonqChatLSP` in `packages/amazonq/src/extensionNode.ts` (line ~53) - Controls whether to use the legacy chat provider or the LSP-based chat provider -3. `amazonqChatLSP` in `packages/amazonq/src/lsp/client.ts` (line ~117) - Controls whether to activate the chat functionality in the LSP client - -This change moved the extension from using the core implementation to the LSP implementation, which lacks IAM credential support. - -### Recent IAM Support in Language-Servers Repository - -A significant recent commit in the language-servers repository adds IAM authentication support: - -**Commit ID:** 16b287b9e -**Author:** sdharani91 -**Date:** 2025-06-26 -**Title:** feat: enable iam auth for agentic chat (#1736) - -Key changes in this commit: - -1. **Environment Variable Flag**: - - ```typescript - // Added function to check for IAM auth mode - export function isUsingIAMAuth(): boolean { - return process.env.USE_IAM_AUTH === 'true' - } - ``` - -2. **Service Manager Selection**: - - ```typescript - // In qAgenticChatServer.ts - amazonQServiceManager = isUsingIAMAuth() ? getOrThrowBaseIAMServiceManager() : getOrThrowBaseTokenServiceManager() - ``` - -3. **IAM Credentials Handling**: - - ```typescript - // Added function to extract IAM credentials - export function getIAMCredentialsFromProvider(credentialsProvider: CredentialsProvider) { - if (!credentialsProvider.hasCredentials('iam')) { - throw new Error('Missing IAM creds') - } - - const credentials = credentialsProvider.getCredentials('iam') as Credentials - return { - accessKeyId: credentials.accessKeyId, - secretAccessKey: credentials.secretAccessKey, - sessionToken: credentials.sessionToken, - } - } - ``` - -4. **Unified Chat Response Interface**: - - ```typescript - // Created types to handle both auth flows - export type ChatCommandInput = SendMessageCommandInput | GenerateAssistantResponseCommandInputCodeWhispererStreaming - export type ChatCommandOutput = - | SendMessageCommandOutput - | GenerateAssistantResponseCommandOutputCodeWhispererStreaming - ``` - -5. **Source Parameter for IAM**: - ```typescript - // Added source parameter for IAM requests - request.source = 'IDE' - ``` - -This commit shows that IAM authentication support has been added to the language-servers repository, but the extension needs to set the `USE_IAM_AUTH` environment variable to `true` when running in SageMaker environments. - -## Proposed Fix - -Based on our investigation of the language-server-runtimes repository and the previous implementation attempt, here's a refined solution: - -1. **Set Environment Variable for IAM Auth**: - - ```typescript - // In packages/core/src/shared/lsp/utils/platform.ts - const env = { ...process.env } - if (isSageMaker()) { - // Check SageMaker cookie to determine auth mode - try { - const result = await vscode.commands.executeCommand('sagemaker.parseCookies') - if (result?.authMode !== 'Sso') { - env.USE_IAM_AUTH = 'true' - getLogger().info(`[SageMaker Debug] Setting USE_IAM_AUTH=true for language server process`) - } - } catch (err) { - getLogger().error('Failed to parse SageMaker cookies: %O', err) - // Default to IAM auth if cookie parsing fails - env.USE_IAM_AUTH = 'true' - getLogger().info(`[SageMaker Debug] Setting USE_IAM_AUTH=true for language server process (default)`) - } - } - - const lspProcess = new ChildProcess(bin, args, { - warnThresholds, - spawnOptions: { env }, - }) - ``` - -2. **Enhance `AmazonQLspAuth` Class** (`packages/amazonq/src/lsp/auth.ts`): - - ```typescript - async refreshConnection(force: boolean = false) { - const activeConnection = this.authUtil.conn - if (this.authUtil.isConnectionValid()) { - if (isSsoConnection(activeConnection)) { - // Existing SSO path - const token = await this.authUtil.getBearerToken() - await (force ? this._updateBearerToken(token) : this.updateBearerToken(token)) - } else if (isSageMaker() && isIamConnection(activeConnection)) { - // SageMaker IAM path - try { - const credentials = await this.authUtil.getCredentials() - if (credentials && credentials.accessKeyId && credentials.secretAccessKey) { - await (force ? this._updateIamCredentials(credentials) : this.updateIamCredentials(credentials)) - } else { - getLogger().error('Invalid IAM credentials: %O', credentials) - } - } catch (err) { - getLogger().error('Failed to get IAM credentials: %O', err) - } - } - } - } - - public updateIamCredentials = onceChanged(this._updateIamCredentials.bind(this)) - private async _updateIamCredentials(credentials: any) { - try { - // Extract only the required fields to match the expected format - const iamCredentials = { - accessKeyId: credentials.accessKeyId, - secretAccessKey: credentials.secretAccessKey, - sessionToken: credentials.sessionToken, - } - - const request = await this.createUpdateIamCredentialsRequest(iamCredentials) - await this.client.sendRequest(iamCredentialsUpdateRequestType.method, request) - this.client.info(`UpdateIamCredentials: Success`) - } catch (err) { - getLogger().error('Failed to update IAM credentials: %O', err) - } - } - ``` - -3. **Update Connection Metadata Handler** (`packages/amazonq/src/lsp/client.ts`): - - ```typescript - client.onRequest(notificationTypes.getConnectionMetadata.method, () => { - // For IAM auth, provide a default startUrl - if (process.env.USE_IAM_AUTH === 'true') { - return { - sso: { - startUrl: 'https://amzn.awsapps.com/start', // Default for IAM auth - }, - } - } - - // For SSO auth, use the actual startUrl - return { - sso: { - startUrl: AuthUtil.instance.auth.startUrl, - }, - } - }) - ``` - -4. **Modify Client Initialization** (`packages/amazonq/src/lsp/client.ts`): - - ```typescript - const useIamAuth = isSageMaker() && process.env.USE_IAM_AUTH === 'true' - - initializationOptions: { - // ... - credentials: { - providesBearerToken: !useIamAuth, - providesIam: useIamAuth, - }, - } - ``` - -5. **Ensure Auto-login Happens Early** (`packages/amazonq/src/lsp/activation.ts`): - ```typescript - export async function activate(ctx: vscode.ExtensionContext): Promise { - try { - // Check for SageMaker and auto-login if needed - if (isSageMaker()) { - try { - const result = await vscode.commands.executeCommand('sagemaker.parseCookies') - if (result?.authMode !== 'Sso') { - // Auto-login with IAM credentials - const sagemakerProfileId = asString({ - credentialSource: 'ec2', - credentialTypeId: 'sagemaker-instance', - }) - await Auth.instance.tryAutoConnect(sagemakerProfileId) - getLogger().info(`Automatically connected with SageMaker IAM credentials`) - } - } catch (err) { - getLogger().error('Failed to parse SageMaker cookies: %O', err) - } - } - - await lspSetupStage('all', async () => { - const installResult = await new AmazonQLspInstaller().resolve() - await lspSetupStage('launch', async () => await startLanguageServer(ctx, installResult.resourcePaths)) - }) - } catch (err) { - const e = err as ToolkitError - void vscode.window.showInformationMessage(`Unable to launch amazonq language server: ${e.message}`) - } - } - ``` - -This refined solution addresses the issues identified in the previous implementation attempt: - -1. It properly checks the SageMaker cookie to determine the auth mode -2. It ensures the IAM credentials are formatted correctly -3. It adds robust error handling -4. It ensures auto-login happens early in the initialization process - -# Next Steps - -## Plan for SageMaker Environment Testing - -We are going to set up a comprehensive testing environment on the SageMaker instance to debug and fix the IAM authentication issue: - -1. **Repository Setup**: - - - Clone aws-toolkit-vscode repository (already done locally) - - Clone language-servers repository to SageMaker instance - - Configure aws-toolkit-vscode to use local build of language-servers instead of downloaded version - -2. **Development Workflow**: - - - Make changes to language-servers codebase directly on SageMaker instance - - Add comprehensive logging throughout the authentication flow - - Test changes immediately in the SageMaker environment where the issue occurs - - Use `amazonq.trace.server` setting for detailed LSP server logs - -3. **Key Areas to Investigate**: - - - Verify that `USE_IAM_AUTH` environment variable is properly set and inherited - - Confirm IAM credentials are correctly passed from extension to language server - - Validate that language server selects correct service manager based on auth mode - - Test that SageMaker cookie detection works properly - -4. **Debugging Strategy**: - - - Follow the exact code execution path from extension activation to LSP authentication - - Add logging at each critical step to trace the authentication flow - - Capture and analyze any errors or failures in the authentication process - - Compare behavior between working SSO environments and failing SageMaker IAM environment - -5. **Implementation Priority**: - - First implement SageMaker cookie detection to determine auth mode - - Add IAM credential handling to AmazonQLspAuth class - - Ensure proper environment variable setting for language server process - - Test and validate the complete authentication flow - -This approach will allow us to make real-time changes and immediately test them in the actual environment where the authentication failure occurs, giving us the best chance to identify and fix the root cause. - -## Critical Issues to Address First - -The document emphasizes that we should **"start back tomorrow by addressing the issues and concerns raised in this document first thing, particularly the SageMaker cookie detection and connection metadata handling for IAM authentication."** - -The most critical missing pieces are: - -1. **SageMaker cookie detection** to determine when to use IAM vs SSO auth -2. **Connection metadata handling** for IAM authentication -3. **Proper error handling** throughout the authentication flow - -These should be implemented before testing the solution in a SageMaker environment. From 41019e69274af0731d51233d2a9f51fa53f69e19 Mon Sep 17 00:00:00 2001 From: Dung Dong Date: Wed, 23 Jul 2025 11:07:27 -0700 Subject: [PATCH 48/69] feat(amazonq): add keyboard shortcut for stop/reject/run commands (#7703) ## Problem Currently, users need to manually click buttons or use the command palette to execute common shell commands (reject/run/stop) in the IDE. This creates friction in the developer workflow, especially for power users who prefer keyboard shortcuts. ## Solution Reopen [Na's PR](https://github.com/aws/aws-toolkit-vscode/pull/7178) that added keyboard shortcuts for reject/run/stop shell commands Update to align with new requirements. Add VS Code feature flag (shortcut) Only available if Q is focused ## Screenshots https://github.com/user-attachments/assets/84df262e-2b92-456d-a8ae-bc2f1fd6318c --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/amazonq/package.json | 33 +++++++++++++++++++ packages/amazonq/src/lsp/chat/commands.ts | 16 ++++++++- packages/amazonq/src/lsp/chat/messages.ts | 14 ++++++++ .../amazonq/src/lsp/chat/webviewProvider.ts | 3 +- packages/amazonq/src/lsp/client.ts | 1 + packages/core/src/shared/vscode/setContext.ts | 1 + 6 files changed, 66 insertions(+), 2 deletions(-) diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index a550b4702bf..9dfc4565f3b 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -560,6 +560,21 @@ ] }, "commands": [ + { + "command": "aws.amazonq.stopCmdExecution", + "title": "Stop Amazon Q Command Execution", + "category": "%AWS.amazonq.title%" + }, + { + "command": "aws.amazonq.runCmdExecution", + "title": "Run Amazon Q Command Execution", + "category": "%AWS.amazonq.title%" + }, + { + "command": "aws.amazonq.rejectCmdExecution", + "title": "Reject Amazon Q Command Execution", + "category": "%AWS.amazonq.title%" + }, { "command": "_aws.amazonq.notifications.dismiss", "title": "%AWS.generic.dismiss%", @@ -850,6 +865,24 @@ } ], "keybindings": [ + { + "command": "aws.amazonq.stopCmdExecution", + "key": "ctrl+shift+backspace", + "mac": "cmd+shift+backspace", + "when": "aws.amazonq.amazonqChatLSP.isFocus" + }, + { + "command": "aws.amazonq.runCmdExecution", + "key": "ctrl+shift+enter", + "mac": "cmd+shift+enter", + "when": "aws.amazonq.amazonqChatLSP.isFocus" + }, + { + "command": "aws.amazonq.rejectCmdExecution", + "key": "ctrl+shift+r", + "mac": "cmd+shift+r", + "when": "aws.amazonq.amazonqChatLSP.isFocus" + }, { "command": "_aws.amazonq.focusChat.keybinding", "win": "win+alt+i", diff --git a/packages/amazonq/src/lsp/chat/commands.ts b/packages/amazonq/src/lsp/chat/commands.ts index 8998144e7e9..83e70b7bae3 100644 --- a/packages/amazonq/src/lsp/chat/commands.ts +++ b/packages/amazonq/src/lsp/chat/commands.ts @@ -59,7 +59,10 @@ export function registerCommands(provider: AmazonQChatViewProvider) { params: {}, }) }) - }) + }), + registerShellCommandShortCut('aws.amazonq.runCmdExecution', 'run-shell-command', provider), + registerShellCommandShortCut('aws.amazonq.rejectCmdExecution', 'reject-shell-command', provider), + registerShellCommandShortCut('aws.amazonq.stopCmdExecution', 'stop-shell-command', provider) ) } @@ -156,3 +159,14 @@ export async function focusAmazonQPanel() { await Commands.tryExecute('aws.amazonq.AmazonQChatView.focus') await Commands.tryExecute('aws.amazonq.AmazonCommonAuth.focus') } + +function registerShellCommandShortCut(commandName: string, buttonId: string, provider: AmazonQChatViewProvider) { + return Commands.register(commandName, async () => { + void focusAmazonQPanel().then(() => { + void provider.webview?.postMessage({ + command: 'aws/chat/executeShellCommandShortCut', + params: { id: buttonId }, + }) + }) + }) +} diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index 57253abc2ba..71de85f90a7 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -88,6 +88,7 @@ import { openUrl, isTextEditor, globals, + setContext, } from 'aws-core-vscode/shared' import { DefaultAmazonQAppInitContext, @@ -490,6 +491,11 @@ export function registerMessageListeners( } default: if (isServerEvent(message.command)) { + if (enterFocus(message.params)) { + await setContext('aws.amazonq.amazonqChatLSP.isFocus', true) + } else if (exitFocus(message.params)) { + await setContext('aws.amazonq.amazonqChatLSP.isFocus', false) + } languageClient.sendNotification(message.command, message.params) } break @@ -699,6 +705,14 @@ function isServerEvent(command: string) { return command.startsWith('aws/chat/') || command === 'telemetry/event' } +function enterFocus(params: any) { + return params.name === 'enterFocus' +} + +function exitFocus(params: any) { + return params.name === 'exitFocus' +} + /** * Decodes partial chat responses from the language server before sending them to mynah UI */ diff --git a/packages/amazonq/src/lsp/chat/webviewProvider.ts b/packages/amazonq/src/lsp/chat/webviewProvider.ts index 7d51648398d..109a6afd10f 100644 --- a/packages/amazonq/src/lsp/chat/webviewProvider.ts +++ b/packages/amazonq/src/lsp/chat/webviewProvider.ts @@ -13,6 +13,7 @@ import { Webview, } from 'vscode' import * as path from 'path' +import * as os from 'os' import { globals, isSageMaker, @@ -149,7 +150,7 @@ export class AmazonQChatViewProvider implements WebviewViewProvider { const vscodeApi = acquireVsCodeApi() const hybridChatConnector = new HybridChatAdapter(${(await AuthUtil.instance.getChatAuthState()).amazonQ === 'connected'},${featureConfigData},${welcomeCount},${disclaimerAcknowledged},${regionProfileString},${disabledCommands},${isSMUS},${isSM},vscodeApi.postMessage) const commands = [hybridChatConnector.initialQuickActions[0]] - qChat = amazonQChat.createChat(vscodeApi, {disclaimerAcknowledged: ${disclaimerAcknowledged}, pairProgrammingAcknowledged: ${pairProgrammingAcknowledged}, agenticMode: true, quickActionCommands: commands, modelSelectionEnabled: ${modelSelectionEnabled}}, hybridChatConnector, ${JSON.stringify(featureConfigData)}); + qChat = amazonQChat.createChat(vscodeApi, {os: "${os.platform()}", disclaimerAcknowledged: ${disclaimerAcknowledged}, pairProgrammingAcknowledged: ${pairProgrammingAcknowledged}, agenticMode: true, quickActionCommands: commands, modelSelectionEnabled: ${modelSelectionEnabled}}, hybridChatConnector, ${JSON.stringify(featureConfigData)}); } window.addEventListener('message', (event) => { /** diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index ab1d0665325..4d052912c8e 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -165,6 +165,7 @@ export async function startLanguageServer( pinnedContextEnabled: true, imageContextEnabled: true, mcp: true, + shortcut: true, reroute: true, modelSelection: true, workspaceFilePath: vscode.workspace.workspaceFile?.fsPath, diff --git a/packages/core/src/shared/vscode/setContext.ts b/packages/core/src/shared/vscode/setContext.ts index 08d651578dc..7cfaf4092f8 100644 --- a/packages/core/src/shared/vscode/setContext.ts +++ b/packages/core/src/shared/vscode/setContext.ts @@ -40,6 +40,7 @@ export type contextKey = | 'gumby.wasQCodeTransformationUsed' | 'amazonq.inline.codelensShortcutEnabled' | 'aws.toolkit.lambda.walkthroughSelected' + | 'aws.amazonq.amazonqChatLSP.isFocus' const contextMap: Partial> = {} From 2fe85cde19b1ada9863f0d9a6802200e7afb55f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=A5=A9=20Flora?= Date: Wed, 23 Jul 2025 11:13:10 -0700 Subject: [PATCH 49/69] Revert "other(amazonq): remove unnecessary notes file" This reverts commit d0082e651d5a0595f88332f83caf6ce6041de632. --- P261194666.md | 630 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 630 insertions(+) create mode 100644 P261194666.md diff --git a/P261194666.md b/P261194666.md new file mode 100644 index 00000000000..feafb3e7ce2 --- /dev/null +++ b/P261194666.md @@ -0,0 +1,630 @@ +# Root-cause SageMaker auth failure in Amazon Q for VSCode after v1.62.0 + +v1.63.0 of the extension introduced agentic chat and moved from directly calling the Q service using an AWS SDK client directly from the extension to indirectly +calling the Q service through the aws-lsp-codewhisperer service in language-servers (aka Flare). + +## Notes + +1. `isSageMaker` function used in many places in aws-toolkit-vscode codebase. `setContext` is used to set whether SMAI (`aws.isSageMaker`) or SMUS (`aws.isSageMakerUnifiedStudio`) is in use so that conditions testing those values can be used in package.json. +2. `aws-toolkit-vscode/packages/amazonq/src/lsp/chat/webviewProvider.ts` passes in booleans for SMAI and SMUS into MynahUI for Q Chat. +3. Files in `aws-toolkit-vscode/packages/core` apply to how the Amazon Q for VSCode extension used to call Q (formerly known as "CodeWhisperer") directly through an SDK client. It was this codebase that SageMaker depended on for authenticating with IAM credentials to Q. Files in `aws-toolkit-vscode/packages/amazonq` apply to how the extension now uses the LSP server (aka aws-lsp-codewhisperer in language-servers aka Flare) for indirectly accessing the Q service. The commit `938bb376647414776a55d7dd7d6761c863764c5c` is primarily what flipped over the extension from using core to amazonq leading to auth breaking for SageMaker. +4. Once we figure out how IAM credentials worked before (likely because core creates it's own SDK client and may do something fancy with auth that aws-lsp-codewhisperer does not), we may find that we need to apply a fix in aws-toolkit-vscode and/or language-servers. +5. Using the core (legacy) Q chat is not an option as the Amazon Q for VSCode team will not be maintaining it. +6. In user settings in VSCode, set `amazonq.trace.server` to `on` for more detailed logs from LSP server. +7. /Users/floralph/Source/P261194666.md contains A LOT of information about researching this issue so far. It can fill your context fast. We will refer to it from time to time and possibly migrate some of the most important information to this doc. You can ask about reading it, but don't read it unless I instruct you to do so and even then, you MUST stay focused only on what you've been asked to do with it. + +## This is CRITICAL + +When trying to root cause the issue, it is ABSOLUTELY CRITICAL that we follow the path of execution related to the CodeWhisperer LSP server from start onwards without dropping the trail. We CANNOT just assume things about other parts of code based on names nor should we assume they are even related to our issue if they are not in the specific code path that we're following. We have to be laser focused on following the code path and looking for issues, not jumping to conclusions and jumping to other code. As we have to use logging as our only means of tracing/debugging in the SageMaker instance, we can use that to follow the path of execution. + +## Repos on disk + +1. /Users/floralph/Source/aws-toolkit-vscode + 1. Branch from breaking commit 938bb376647414776a55d7dd7d6761c863764c5c for experimenting on: bug/sm-auth + 2. Branch where you tried to add IAM creds using /Users/floralph/Source/P261194666.md: floralph/P261194666 +2. /Users/floralph/Source/language-server-runtimes +3. /Users/floralph/Source/language-servers + +If we absolutely need to look at MynahUI code, I can try to track it down. It might be lingering in one of the repos above though. + +## Important files likely related to the issue and fix + +- aws-toolkit-vscode/packages/amazonq/src/extensionNode.ts + +## Git Bisect Results - Breaking Commit + +**Commit ID:** `938bb376647414776a55d7dd7d6761c863764c5c` +**Author:** Josh Pinkney +**Date:** Not specified in bisect output +**Title:** Enable Amazon Q LSP experiments by default + +### What This Commit Changed + +This commit flipped three experiment flags from `false` to `true`, fundamentally changing Amazon Q's architecture from legacy chat system to LSP-based system: + +```diff +diff --git a/packages/amazonq/src/extension.ts b/packages/amazonq/src/extension.ts +index fe5ce809c..345e6e646 100644 +--- a/packages/amazonq/src/extension.ts ++++ b/packages/amazonq/src/extension.ts +@@ -119,7 +119,7 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is + } + // This contains every lsp agnostic things (auth, security scan, code scan) + await activateCodeWhisperer(extContext as ExtContext) +- if (Experiments.instance.get('amazonqLSP', false)) { ++ if (Experiments.instance.get('amazonqLSP', true)) { + await activateAmazonqLsp(context) + } + +diff --git a/packages/amazonq/src/extensionNode.ts b/packages/amazonq/src/extensionNode.ts +index d3e98b025..5a8b5082c 100644 +--- a/packages/amazonq/src/extensionNode.ts ++++ b/packages/amazonq/src/extensionNode.ts +@@ -53,7 +53,7 @@ async function activateAmazonQNode(context: vscode.ExtensionContext) { + extensionContext: context, + } + +- if (!Experiments.instance.get('amazonqChatLSP', false)) { ++ if (!Experiments.instance.get('amazonqChatLSP', true)) { + const appInitContext = DefaultAmazonQAppInitContext.instance + const provider = new AmazonQChatViewProvider( + context, +diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts +index e45a3fdac..12341ff17 100644 +--- a/packages/amazonq/src/lsp/client.ts ++++ b/packages/amazonq/src/lsp/client.ts +@@ -117,7 +117,7 @@ export async function startLanguageServer( + ) + } + +- if (Experiments.instance.get('amazonqChatLSP', false)) { ++ if (Experiments.instance.get('amazonqChatLSP', true)) { + await activate(client, encryptionKey, resourcePaths.ui) + } +``` + +### Commit: 6ce383258 - "feat(sagemaker): free tier Q Chat with auto-login for iam users and login option for pro tier users (#5886)" + +This commit shows how the Amazon Q for VSCode extension was updated (core only as the LSP server was not used by this extension at the time) to use +IAM credentials from SageMaker. + +**Author:** Ahmed Ali (azkali) +**Date:** October 29, 2024 + +#### Key Auth-Related Changes: + +1. **SageMaker Cookie-Based Authentication Detection** in `packages/core/src/auth/activation.ts`: + + ```typescript + interface SagemakerCookie { + authMode?: 'Sso' | 'Iam' + } + + export async function initialize(loginManager: LoginManager): Promise { + if (isAmazonQ() && isSageMaker()) { + // The command `sagemaker.parseCookies` is registered in VS Code Sagemaker environment. + const result = (await vscode.commands.executeCommand('sagemaker.parseCookies')) as SagemakerCookie + if (result.authMode !== 'Sso') { + initializeCredentialsProviderManager() + } + } + ``` + +2. **New Credentials Provider Manager Initialization** in `packages/core/src/auth/utils.ts`: + + ```typescript + export function initializeCredentialsProviderManager() { + const manager = CredentialsProviderManager.getInstance() + manager.addProviderFactory(new SharedCredentialsProviderFactory()) + manager.addProviders( + new Ec2CredentialsProvider(), + new EcsCredentialsProvider(), + new EnvVarsCredentialsProvider() + ) + } + ``` + +3. **Modified CodeWhisperer Auth Validation** in `packages/core/src/codewhisperer/util/authUtil.ts`: + + ```typescript + // BEFORE: + if (isSageMaker()) { + return isIamConnection(conn) + } + + // AFTER: + return ( + (isSageMaker() && isIamConnection(conn)) || + (isCloud9('codecatalyst') && isIamConnection(conn)) || + (isSsoConnection(conn) && hasScopes(conn, codeWhispererCoreScopes)) + ) + ``` + +4. **Amazon Q Connection Validation Enhanced**: + + ```typescript + export const isValidAmazonQConnection = (conn?: Connection): conn is Connection => { + return ( + (isSageMaker() && isIamConnection(conn)) || + ((isSsoConnection(conn) || isBuilderIdConnection(conn)) && + isValidCodeWhispererCoreConnection(conn) && + hasScopes(conn, amazonQScopes)) + ) + } + ``` + +5. **Dual Chat Client Implementation** in `packages/core/src/codewhispererChat/clients/chat/v0/chat.ts`: + + ```typescript + // New IAM-based chat method + async chatIam(chatRequest: SendMessageRequest): Promise { + const client = await createQDeveloperStreamingClient() + const response = await client.sendMessage(chatRequest) + // ... session handling + } + + // Existing SSO-based chat method + async chatSso(chatRequest: GenerateAssistantResponseRequest): Promise { + const client = await createCodeWhispererChatStreamingClient() + // ... existing logic + } + ``` + +6. **Chat Controller Route Selection** in `packages/core/src/codewhispererChat/controllers/chat/controller.ts`: + ```typescript + if (isSsoConnection(AuthUtil.instance.conn)) { + const { $metadata, generateAssistantResponseResponse } = await session.chatSso(request) + response = { $metadata: $metadata, message: generateAssistantResponseResponse } + } else { + const { $metadata, sendMessageResponse } = await session.chatIam(request as SendMessageRequest) + response = { $metadata: $metadata, message: sendMessageResponse } + } + ``` + +#### Key Findings: + +- **Two separate Q API clients**: `createQDeveloperStreamingClient()` for IAM, `createCodeWhispererChatStreamingClient()` for SSO +- **SageMaker cookie-based auth detection**: Uses `sagemaker.parseCookies` command to determine auth mode +- **Automatic credential provider setup**: Initializes EC2, ECS, and environment variable credential providers for IAM users +- **Route selection based on connection type**: SSO connections use old client, IAM connections use new Q Developer client + +## Related Historical Fix - CodeWhisperer SageMaker Authentication + +**Commit ID:** `b125a1bd3b135344d2aa24961e746a10e55702c6` +**Author:** Lei Gao +**Date:** March 18, 2024 +**Title:** "fix(codewhisperer): completion error in sagemaker #4545" + +### Problem Identified + +In SageMaker Code Editor, CodeWhisperer was failing with: + +``` +Unexpected key 'optOutPreference' found in params +``` + +### Root Cause + +SageMaker environments require **GenerateRecommendation** API calls instead of **ListRecommendation** API calls for SigV4 authentication to work properly. + +### Fix Applied + +Modified `packages/core/src/codewhisperer/service/recommendationHandler.ts`: + +```typescript +// BEFORE: Used pagination logic that triggered ListRecommendation +if (pagination) { + // ListRecommendation request - FAILS in SageMaker +} + +// AFTER: SageMaker detection forces GenerateRecommendation +if (pagination && !isSM) { + // Added !isSM condition + // ListRecommendation only for non-SageMaker +} else { + // GenerateRecommendation for SageMaker (and non-pagination cases) +} +``` + +## Key Insights + +### Pattern Recognition + +Both issues share the same fundamental problem: **SageMaker environments have different API authentication requirements** that break standard AWS SDK calls. + +### Hypothesis for Current Issue + +The Amazon Q LSP (enabled by default in v1.63.0) is likely making API calls that: + +1. Work fine in standard environments +2. Fail in SageMaker due to different credential passing mechanisms +3. Require SageMaker-specific request formatting (similar to CodeWhisperer fix) + +# Files + +## aws-toolkit-vscode/packages/amazonq/src/extension.ts + +Starts both the old "core" CodeWhisperer code with `await activateCodeWhisperer(extContext as ExtContext)` on line ~121, followed by the new LSP code for Amazon Q. Maybe the dev team is slowly migrating functionality from core to amazonq and this is how they have both running at once. The code below is one of 3 places where the 'amazonqLSP' experiment is set to on by default in the commit that broke SageMaker auth. + +```typescript +// This contains every lsp agnostic things (auth, security scan, code scan) +await activateCodeWhisperer(extContext as ExtContext) +if (Experiments.instance.get('amazonqLSP', true)) { + await activateAmazonqLsp(context) +} +``` + +`activateAmazonqLsp` downloads and installs the language-servers bundle then executes the CodeWhisperer start up script (we should find the specific name and path) and initializes the LSP server, including auth set up. + +## aws-toolkit-vscode/packages/core/src/auth/activation.ts + +This file appears critical to how the SageMaker auth worked. It is in core however, and not clear whether it is even in the code path for the LSP server or not. We should review this file closely to understand how IAM credentials worked as it should inform us on what needs to change in the amazonq package to support IAM credentials as well. The `sagemaker.parseCookies` code here also seems important in determining whether the SageMaker instance wants to use IAM or SSO, so that should probably be carried over into the amazonq package as well. + +The `Auth.instance.onDidChangeActiveConnection` handler code should be investigated further. It's not clear if it has anything to do with auth to Q or if it's just older "toolkit"-related auth stuff. + +## aws-toolkit-vscode/packages/core/src/auth/utils.ts + +This is a collection of utility functions and many are related to auth/security. However, it appears to be `initializeCredentialsProviderManager` in our code path, called by `aws-toolkit-vscode/packages/core/src/auth/activation.ts` that may be of importance. We should determine if we need this or similar functionality in amazonq package or if this is just a hold-over that updates the old "toolkit" (i.e. non-Amazon Q parts of the extension) stuff. + +## aws-toolkit-vscode/packages/amazonq/src/lsp/client.ts + +1. line ~68 sets `providesBearerToken: true` but doesn't appear to have anything similar for IAM credentials. +2. line ~93 to the end starts auth for LSP using the `AmazonQLspAuth` class. This all appears to be for SSO tokens, nothing for IAM credentials. + +## aws-toolkit-vscode/packages/amazonq/src/lsp/auth.ts + +1. Defines `AmazonQLspAuth` class that is only for SSO tokens, nothing about IAM credentials. +2. Some SSO token related functions are exported, but nothing similar for IAM credentials. + +## aws-toolkit-vscode/packages/core/src/codewhisperer/activation.ts + +`activate` in the old "core" Q implementation is called by `aws-toolkit-vscode/packages/amazonq/src/extension.ts` line ~121. + +Suspcious code that is still running in `activate` function. How does this not interfer with the new auth code in the amazonq package? + +```typescript +// initialize AuthUtil earlier to make sure it can listen to connection change events. +const auth = AuthUtil.instance +auth.initCodeWhispererHooks() +``` + +Further down in this file it still creates and uses `onst client = new codewhispererClient.DefaultCodeWhispererClient()` which makes it appear to be using both direct calls from the extension as well as the LSP to access the Q service. This bears further investigation into what this code is actually doing. + +## aws-toolkit-vscode/packages/core/src/codewhisperer/client/codewhisperer.ts + +This is the old "core" CodeWhisperer service client. There is likely important code here that informs how IAM authentication works with the service client that may be missing in the language-servers CodeWhisperer client. If my hunch is correct in that the "core" code is still in use for what hasn't been migrated yet, this code may not be actively used for Q Chat which was migrated (see the Experiments flags defaulting to true in the breaking commit) to the amazonq package and should be using the auth there and in language-servers. + +## aws-toolkit-vscode/packages/amazonq/src/extensionNode.ts + +The code below is one of 3 places where the 'amazonqChatLSP' experiment is set to on by default in the commit that broke SageMaker auth. There is some "auth"-related code in this file that should be investigated further to determine if it has any impact on the broken SageMaker auth. It isn't obvious that it does or doesn't. It may just be used in the MynahUI Q Chat webview, and not the LSP server. + +```typescript +if (!Experiments.instance.get('amazonqChatLSP', true)) { +``` + +## aws-toolkit-vscode/packages/core/src/auth/auth.ts + +This file was updated recently for the SMUS project. It may not be directly related to the broken SageMaker auth issue, but the comments on the added/changed functions are suspicious regarding how credentials are received. SMUS may be adding a different way to get IAM credentials than what SMAI used. + +```typescript +/** + * Returns true if credentials are provided by the environment (ex. via ~/.aws/) + * + * @param isC9 boolean for if Cloud9 is host + * @param isSM boolean for if SageMaker is host + * @returns boolean for if C9 "OR" SM + */ +export function hasVendedIamCredentials(isC9?: boolean, isSM?: boolean) { + isC9 ??= isCloud9() + isSM ??= isSageMaker() + return isSM || isC9 +} + +/** + * Returns true if credentials are provided by the metadata files in environment (ex. for IAM via ~/.aws/ and in a future case with SSO, from /cache or /sso) + * @param isSMUS boolean if SageMaker Unified Studio is host + * @returns boolean if SMUS + */ +export function hasVendedCredentialsFromMetadata(isSMUS?: boolean) { + isSMUS ??= isSageMaker('SMUS') + return isSMUS +} +``` + +There is also A LOT of other auth related functionality here, but it's in "core" and may not be directly related the code paths for LSP and breaking auth in SageMaker. + +## aws-toolkit-vscode/packages/core/src/codewhisperer/util/authUtil.ts + +There is some `isSageMaker`-related code here that we should investigate. It appears to be important to auth with SageMaker, but it's not clear if it or similar code is needed and has made it into the amazonq package. Once we confirm any of this code is in our code path of concern, it should be investigated further. + +## aws-toolkit-vscode/packages/amazonq/src/lsp/chat/webviewProvider.ts + +While there is special SageMaker handling in this file, it is not clear if it is related to IAM auth issues with the LSP or it is just related to the chat UI. If we find it is in our code path, we can investigate further. + +# Proposed Fix for SageMaker IAM Authentication in Amazon Q LSP + +> **NOTE:** We should start back tomorrow by addressing the issues and concerns raised in this document first thing, particularly the SageMaker cookie detection and connection metadata handling for IAM authentication. + +## Issue Summary + +The Amazon Q extension for VSCode fails to authenticate in SageMaker environments after v1.62.0 due to a change in architecture. The extension moved from directly calling the Q service using an AWS SDK client to indirectly calling it through the aws-lsp-codewhisperer service (Flare). While the old implementation had specific handling for SageMaker IAM credentials, the new LSP-based implementation only supports SSO token authentication. + +## Root Cause Analysis + +### Breaking Change + +Commit `938bb376647414776a55d7dd7d6761c863764c5c` enabled three experiment flags by default: + +1. `amazonqLSP` in `packages/amazonq/src/extension.ts` (line ~119) - Controls whether to activate the Amazon Q LSP +2. `amazonqChatLSP` in `packages/amazonq/src/extensionNode.ts` (line ~53) - Controls whether to use the legacy chat provider or the LSP-based chat provider +3. `amazonqChatLSP` in `packages/amazonq/src/lsp/client.ts` (line ~117) - Controls whether to activate the chat functionality in the LSP client + +This change moved the extension from using the core implementation to the LSP implementation, which lacks IAM credential support. + +### Recent IAM Support in Language-Servers Repository + +A significant recent commit in the language-servers repository adds IAM authentication support: + +**Commit ID:** 16b287b9e +**Author:** sdharani91 +**Date:** 2025-06-26 +**Title:** feat: enable iam auth for agentic chat (#1736) + +Key changes in this commit: + +1. **Environment Variable Flag**: + + ```typescript + // Added function to check for IAM auth mode + export function isUsingIAMAuth(): boolean { + return process.env.USE_IAM_AUTH === 'true' + } + ``` + +2. **Service Manager Selection**: + + ```typescript + // In qAgenticChatServer.ts + amazonQServiceManager = isUsingIAMAuth() ? getOrThrowBaseIAMServiceManager() : getOrThrowBaseTokenServiceManager() + ``` + +3. **IAM Credentials Handling**: + + ```typescript + // Added function to extract IAM credentials + export function getIAMCredentialsFromProvider(credentialsProvider: CredentialsProvider) { + if (!credentialsProvider.hasCredentials('iam')) { + throw new Error('Missing IAM creds') + } + + const credentials = credentialsProvider.getCredentials('iam') as Credentials + return { + accessKeyId: credentials.accessKeyId, + secretAccessKey: credentials.secretAccessKey, + sessionToken: credentials.sessionToken, + } + } + ``` + +4. **Unified Chat Response Interface**: + + ```typescript + // Created types to handle both auth flows + export type ChatCommandInput = SendMessageCommandInput | GenerateAssistantResponseCommandInputCodeWhispererStreaming + export type ChatCommandOutput = + | SendMessageCommandOutput + | GenerateAssistantResponseCommandOutputCodeWhispererStreaming + ``` + +5. **Source Parameter for IAM**: + ```typescript + // Added source parameter for IAM requests + request.source = 'IDE' + ``` + +This commit shows that IAM authentication support has been added to the language-servers repository, but the extension needs to set the `USE_IAM_AUTH` environment variable to `true` when running in SageMaker environments. + +## Proposed Fix + +Based on our investigation of the language-server-runtimes repository and the previous implementation attempt, here's a refined solution: + +1. **Set Environment Variable for IAM Auth**: + + ```typescript + // In packages/core/src/shared/lsp/utils/platform.ts + const env = { ...process.env } + if (isSageMaker()) { + // Check SageMaker cookie to determine auth mode + try { + const result = await vscode.commands.executeCommand('sagemaker.parseCookies') + if (result?.authMode !== 'Sso') { + env.USE_IAM_AUTH = 'true' + getLogger().info(`[SageMaker Debug] Setting USE_IAM_AUTH=true for language server process`) + } + } catch (err) { + getLogger().error('Failed to parse SageMaker cookies: %O', err) + // Default to IAM auth if cookie parsing fails + env.USE_IAM_AUTH = 'true' + getLogger().info(`[SageMaker Debug] Setting USE_IAM_AUTH=true for language server process (default)`) + } + } + + const lspProcess = new ChildProcess(bin, args, { + warnThresholds, + spawnOptions: { env }, + }) + ``` + +2. **Enhance `AmazonQLspAuth` Class** (`packages/amazonq/src/lsp/auth.ts`): + + ```typescript + async refreshConnection(force: boolean = false) { + const activeConnection = this.authUtil.conn + if (this.authUtil.isConnectionValid()) { + if (isSsoConnection(activeConnection)) { + // Existing SSO path + const token = await this.authUtil.getBearerToken() + await (force ? this._updateBearerToken(token) : this.updateBearerToken(token)) + } else if (isSageMaker() && isIamConnection(activeConnection)) { + // SageMaker IAM path + try { + const credentials = await this.authUtil.getCredentials() + if (credentials && credentials.accessKeyId && credentials.secretAccessKey) { + await (force ? this._updateIamCredentials(credentials) : this.updateIamCredentials(credentials)) + } else { + getLogger().error('Invalid IAM credentials: %O', credentials) + } + } catch (err) { + getLogger().error('Failed to get IAM credentials: %O', err) + } + } + } + } + + public updateIamCredentials = onceChanged(this._updateIamCredentials.bind(this)) + private async _updateIamCredentials(credentials: any) { + try { + // Extract only the required fields to match the expected format + const iamCredentials = { + accessKeyId: credentials.accessKeyId, + secretAccessKey: credentials.secretAccessKey, + sessionToken: credentials.sessionToken, + } + + const request = await this.createUpdateIamCredentialsRequest(iamCredentials) + await this.client.sendRequest(iamCredentialsUpdateRequestType.method, request) + this.client.info(`UpdateIamCredentials: Success`) + } catch (err) { + getLogger().error('Failed to update IAM credentials: %O', err) + } + } + ``` + +3. **Update Connection Metadata Handler** (`packages/amazonq/src/lsp/client.ts`): + + ```typescript + client.onRequest(notificationTypes.getConnectionMetadata.method, () => { + // For IAM auth, provide a default startUrl + if (process.env.USE_IAM_AUTH === 'true') { + return { + sso: { + startUrl: 'https://amzn.awsapps.com/start', // Default for IAM auth + }, + } + } + + // For SSO auth, use the actual startUrl + return { + sso: { + startUrl: AuthUtil.instance.auth.startUrl, + }, + } + }) + ``` + +4. **Modify Client Initialization** (`packages/amazonq/src/lsp/client.ts`): + + ```typescript + const useIamAuth = isSageMaker() && process.env.USE_IAM_AUTH === 'true' + + initializationOptions: { + // ... + credentials: { + providesBearerToken: !useIamAuth, + providesIam: useIamAuth, + }, + } + ``` + +5. **Ensure Auto-login Happens Early** (`packages/amazonq/src/lsp/activation.ts`): + ```typescript + export async function activate(ctx: vscode.ExtensionContext): Promise { + try { + // Check for SageMaker and auto-login if needed + if (isSageMaker()) { + try { + const result = await vscode.commands.executeCommand('sagemaker.parseCookies') + if (result?.authMode !== 'Sso') { + // Auto-login with IAM credentials + const sagemakerProfileId = asString({ + credentialSource: 'ec2', + credentialTypeId: 'sagemaker-instance', + }) + await Auth.instance.tryAutoConnect(sagemakerProfileId) + getLogger().info(`Automatically connected with SageMaker IAM credentials`) + } + } catch (err) { + getLogger().error('Failed to parse SageMaker cookies: %O', err) + } + } + + await lspSetupStage('all', async () => { + const installResult = await new AmazonQLspInstaller().resolve() + await lspSetupStage('launch', async () => await startLanguageServer(ctx, installResult.resourcePaths)) + }) + } catch (err) { + const e = err as ToolkitError + void vscode.window.showInformationMessage(`Unable to launch amazonq language server: ${e.message}`) + } + } + ``` + +This refined solution addresses the issues identified in the previous implementation attempt: + +1. It properly checks the SageMaker cookie to determine the auth mode +2. It ensures the IAM credentials are formatted correctly +3. It adds robust error handling +4. It ensures auto-login happens early in the initialization process + +# Next Steps + +## Plan for SageMaker Environment Testing + +We are going to set up a comprehensive testing environment on the SageMaker instance to debug and fix the IAM authentication issue: + +1. **Repository Setup**: + + - Clone aws-toolkit-vscode repository (already done locally) + - Clone language-servers repository to SageMaker instance + - Configure aws-toolkit-vscode to use local build of language-servers instead of downloaded version + +2. **Development Workflow**: + + - Make changes to language-servers codebase directly on SageMaker instance + - Add comprehensive logging throughout the authentication flow + - Test changes immediately in the SageMaker environment where the issue occurs + - Use `amazonq.trace.server` setting for detailed LSP server logs + +3. **Key Areas to Investigate**: + + - Verify that `USE_IAM_AUTH` environment variable is properly set and inherited + - Confirm IAM credentials are correctly passed from extension to language server + - Validate that language server selects correct service manager based on auth mode + - Test that SageMaker cookie detection works properly + +4. **Debugging Strategy**: + + - Follow the exact code execution path from extension activation to LSP authentication + - Add logging at each critical step to trace the authentication flow + - Capture and analyze any errors or failures in the authentication process + - Compare behavior between working SSO environments and failing SageMaker IAM environment + +5. **Implementation Priority**: + - First implement SageMaker cookie detection to determine auth mode + - Add IAM credential handling to AmazonQLspAuth class + - Ensure proper environment variable setting for language server process + - Test and validate the complete authentication flow + +This approach will allow us to make real-time changes and immediately test them in the actual environment where the authentication failure occurs, giving us the best chance to identify and fix the root cause. + +## Critical Issues to Address First + +The document emphasizes that we should **"start back tomorrow by addressing the issues and concerns raised in this document first thing, particularly the SageMaker cookie detection and connection metadata handling for IAM authentication."** + +The most critical missing pieces are: + +1. **SageMaker cookie detection** to determine when to use IAM vs SSO auth +2. **Connection metadata handling** for IAM authentication +3. **Proper error handling** throughout the authentication flow + +These should be implemented before testing the solution in a SageMaker environment. From 109a6747baac4bb63f580653a004ad69c0bf76d3 Mon Sep 17 00:00:00 2001 From: Ralph Flora Date: Wed, 23 Jul 2025 12:58:39 -0700 Subject: [PATCH 50/69] revert(amazonq): remove unnecessary notes file (#7737) ## Problem Unnecessary notes doc not needed in repo. ## Solution Remove doc. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- P261194666.md | 630 -------------------------------------------------- 1 file changed, 630 deletions(-) delete mode 100644 P261194666.md diff --git a/P261194666.md b/P261194666.md deleted file mode 100644 index feafb3e7ce2..00000000000 --- a/P261194666.md +++ /dev/null @@ -1,630 +0,0 @@ -# Root-cause SageMaker auth failure in Amazon Q for VSCode after v1.62.0 - -v1.63.0 of the extension introduced agentic chat and moved from directly calling the Q service using an AWS SDK client directly from the extension to indirectly -calling the Q service through the aws-lsp-codewhisperer service in language-servers (aka Flare). - -## Notes - -1. `isSageMaker` function used in many places in aws-toolkit-vscode codebase. `setContext` is used to set whether SMAI (`aws.isSageMaker`) or SMUS (`aws.isSageMakerUnifiedStudio`) is in use so that conditions testing those values can be used in package.json. -2. `aws-toolkit-vscode/packages/amazonq/src/lsp/chat/webviewProvider.ts` passes in booleans for SMAI and SMUS into MynahUI for Q Chat. -3. Files in `aws-toolkit-vscode/packages/core` apply to how the Amazon Q for VSCode extension used to call Q (formerly known as "CodeWhisperer") directly through an SDK client. It was this codebase that SageMaker depended on for authenticating with IAM credentials to Q. Files in `aws-toolkit-vscode/packages/amazonq` apply to how the extension now uses the LSP server (aka aws-lsp-codewhisperer in language-servers aka Flare) for indirectly accessing the Q service. The commit `938bb376647414776a55d7dd7d6761c863764c5c` is primarily what flipped over the extension from using core to amazonq leading to auth breaking for SageMaker. -4. Once we figure out how IAM credentials worked before (likely because core creates it's own SDK client and may do something fancy with auth that aws-lsp-codewhisperer does not), we may find that we need to apply a fix in aws-toolkit-vscode and/or language-servers. -5. Using the core (legacy) Q chat is not an option as the Amazon Q for VSCode team will not be maintaining it. -6. In user settings in VSCode, set `amazonq.trace.server` to `on` for more detailed logs from LSP server. -7. /Users/floralph/Source/P261194666.md contains A LOT of information about researching this issue so far. It can fill your context fast. We will refer to it from time to time and possibly migrate some of the most important information to this doc. You can ask about reading it, but don't read it unless I instruct you to do so and even then, you MUST stay focused only on what you've been asked to do with it. - -## This is CRITICAL - -When trying to root cause the issue, it is ABSOLUTELY CRITICAL that we follow the path of execution related to the CodeWhisperer LSP server from start onwards without dropping the trail. We CANNOT just assume things about other parts of code based on names nor should we assume they are even related to our issue if they are not in the specific code path that we're following. We have to be laser focused on following the code path and looking for issues, not jumping to conclusions and jumping to other code. As we have to use logging as our only means of tracing/debugging in the SageMaker instance, we can use that to follow the path of execution. - -## Repos on disk - -1. /Users/floralph/Source/aws-toolkit-vscode - 1. Branch from breaking commit 938bb376647414776a55d7dd7d6761c863764c5c for experimenting on: bug/sm-auth - 2. Branch where you tried to add IAM creds using /Users/floralph/Source/P261194666.md: floralph/P261194666 -2. /Users/floralph/Source/language-server-runtimes -3. /Users/floralph/Source/language-servers - -If we absolutely need to look at MynahUI code, I can try to track it down. It might be lingering in one of the repos above though. - -## Important files likely related to the issue and fix - -- aws-toolkit-vscode/packages/amazonq/src/extensionNode.ts - -## Git Bisect Results - Breaking Commit - -**Commit ID:** `938bb376647414776a55d7dd7d6761c863764c5c` -**Author:** Josh Pinkney -**Date:** Not specified in bisect output -**Title:** Enable Amazon Q LSP experiments by default - -### What This Commit Changed - -This commit flipped three experiment flags from `false` to `true`, fundamentally changing Amazon Q's architecture from legacy chat system to LSP-based system: - -```diff -diff --git a/packages/amazonq/src/extension.ts b/packages/amazonq/src/extension.ts -index fe5ce809c..345e6e646 100644 ---- a/packages/amazonq/src/extension.ts -+++ b/packages/amazonq/src/extension.ts -@@ -119,7 +119,7 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is - } - // This contains every lsp agnostic things (auth, security scan, code scan) - await activateCodeWhisperer(extContext as ExtContext) -- if (Experiments.instance.get('amazonqLSP', false)) { -+ if (Experiments.instance.get('amazonqLSP', true)) { - await activateAmazonqLsp(context) - } - -diff --git a/packages/amazonq/src/extensionNode.ts b/packages/amazonq/src/extensionNode.ts -index d3e98b025..5a8b5082c 100644 ---- a/packages/amazonq/src/extensionNode.ts -+++ b/packages/amazonq/src/extensionNode.ts -@@ -53,7 +53,7 @@ async function activateAmazonQNode(context: vscode.ExtensionContext) { - extensionContext: context, - } - -- if (!Experiments.instance.get('amazonqChatLSP', false)) { -+ if (!Experiments.instance.get('amazonqChatLSP', true)) { - const appInitContext = DefaultAmazonQAppInitContext.instance - const provider = new AmazonQChatViewProvider( - context, -diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts -index e45a3fdac..12341ff17 100644 ---- a/packages/amazonq/src/lsp/client.ts -+++ b/packages/amazonq/src/lsp/client.ts -@@ -117,7 +117,7 @@ export async function startLanguageServer( - ) - } - -- if (Experiments.instance.get('amazonqChatLSP', false)) { -+ if (Experiments.instance.get('amazonqChatLSP', true)) { - await activate(client, encryptionKey, resourcePaths.ui) - } -``` - -### Commit: 6ce383258 - "feat(sagemaker): free tier Q Chat with auto-login for iam users and login option for pro tier users (#5886)" - -This commit shows how the Amazon Q for VSCode extension was updated (core only as the LSP server was not used by this extension at the time) to use -IAM credentials from SageMaker. - -**Author:** Ahmed Ali (azkali) -**Date:** October 29, 2024 - -#### Key Auth-Related Changes: - -1. **SageMaker Cookie-Based Authentication Detection** in `packages/core/src/auth/activation.ts`: - - ```typescript - interface SagemakerCookie { - authMode?: 'Sso' | 'Iam' - } - - export async function initialize(loginManager: LoginManager): Promise { - if (isAmazonQ() && isSageMaker()) { - // The command `sagemaker.parseCookies` is registered in VS Code Sagemaker environment. - const result = (await vscode.commands.executeCommand('sagemaker.parseCookies')) as SagemakerCookie - if (result.authMode !== 'Sso') { - initializeCredentialsProviderManager() - } - } - ``` - -2. **New Credentials Provider Manager Initialization** in `packages/core/src/auth/utils.ts`: - - ```typescript - export function initializeCredentialsProviderManager() { - const manager = CredentialsProviderManager.getInstance() - manager.addProviderFactory(new SharedCredentialsProviderFactory()) - manager.addProviders( - new Ec2CredentialsProvider(), - new EcsCredentialsProvider(), - new EnvVarsCredentialsProvider() - ) - } - ``` - -3. **Modified CodeWhisperer Auth Validation** in `packages/core/src/codewhisperer/util/authUtil.ts`: - - ```typescript - // BEFORE: - if (isSageMaker()) { - return isIamConnection(conn) - } - - // AFTER: - return ( - (isSageMaker() && isIamConnection(conn)) || - (isCloud9('codecatalyst') && isIamConnection(conn)) || - (isSsoConnection(conn) && hasScopes(conn, codeWhispererCoreScopes)) - ) - ``` - -4. **Amazon Q Connection Validation Enhanced**: - - ```typescript - export const isValidAmazonQConnection = (conn?: Connection): conn is Connection => { - return ( - (isSageMaker() && isIamConnection(conn)) || - ((isSsoConnection(conn) || isBuilderIdConnection(conn)) && - isValidCodeWhispererCoreConnection(conn) && - hasScopes(conn, amazonQScopes)) - ) - } - ``` - -5. **Dual Chat Client Implementation** in `packages/core/src/codewhispererChat/clients/chat/v0/chat.ts`: - - ```typescript - // New IAM-based chat method - async chatIam(chatRequest: SendMessageRequest): Promise { - const client = await createQDeveloperStreamingClient() - const response = await client.sendMessage(chatRequest) - // ... session handling - } - - // Existing SSO-based chat method - async chatSso(chatRequest: GenerateAssistantResponseRequest): Promise { - const client = await createCodeWhispererChatStreamingClient() - // ... existing logic - } - ``` - -6. **Chat Controller Route Selection** in `packages/core/src/codewhispererChat/controllers/chat/controller.ts`: - ```typescript - if (isSsoConnection(AuthUtil.instance.conn)) { - const { $metadata, generateAssistantResponseResponse } = await session.chatSso(request) - response = { $metadata: $metadata, message: generateAssistantResponseResponse } - } else { - const { $metadata, sendMessageResponse } = await session.chatIam(request as SendMessageRequest) - response = { $metadata: $metadata, message: sendMessageResponse } - } - ``` - -#### Key Findings: - -- **Two separate Q API clients**: `createQDeveloperStreamingClient()` for IAM, `createCodeWhispererChatStreamingClient()` for SSO -- **SageMaker cookie-based auth detection**: Uses `sagemaker.parseCookies` command to determine auth mode -- **Automatic credential provider setup**: Initializes EC2, ECS, and environment variable credential providers for IAM users -- **Route selection based on connection type**: SSO connections use old client, IAM connections use new Q Developer client - -## Related Historical Fix - CodeWhisperer SageMaker Authentication - -**Commit ID:** `b125a1bd3b135344d2aa24961e746a10e55702c6` -**Author:** Lei Gao -**Date:** March 18, 2024 -**Title:** "fix(codewhisperer): completion error in sagemaker #4545" - -### Problem Identified - -In SageMaker Code Editor, CodeWhisperer was failing with: - -``` -Unexpected key 'optOutPreference' found in params -``` - -### Root Cause - -SageMaker environments require **GenerateRecommendation** API calls instead of **ListRecommendation** API calls for SigV4 authentication to work properly. - -### Fix Applied - -Modified `packages/core/src/codewhisperer/service/recommendationHandler.ts`: - -```typescript -// BEFORE: Used pagination logic that triggered ListRecommendation -if (pagination) { - // ListRecommendation request - FAILS in SageMaker -} - -// AFTER: SageMaker detection forces GenerateRecommendation -if (pagination && !isSM) { - // Added !isSM condition - // ListRecommendation only for non-SageMaker -} else { - // GenerateRecommendation for SageMaker (and non-pagination cases) -} -``` - -## Key Insights - -### Pattern Recognition - -Both issues share the same fundamental problem: **SageMaker environments have different API authentication requirements** that break standard AWS SDK calls. - -### Hypothesis for Current Issue - -The Amazon Q LSP (enabled by default in v1.63.0) is likely making API calls that: - -1. Work fine in standard environments -2. Fail in SageMaker due to different credential passing mechanisms -3. Require SageMaker-specific request formatting (similar to CodeWhisperer fix) - -# Files - -## aws-toolkit-vscode/packages/amazonq/src/extension.ts - -Starts both the old "core" CodeWhisperer code with `await activateCodeWhisperer(extContext as ExtContext)` on line ~121, followed by the new LSP code for Amazon Q. Maybe the dev team is slowly migrating functionality from core to amazonq and this is how they have both running at once. The code below is one of 3 places where the 'amazonqLSP' experiment is set to on by default in the commit that broke SageMaker auth. - -```typescript -// This contains every lsp agnostic things (auth, security scan, code scan) -await activateCodeWhisperer(extContext as ExtContext) -if (Experiments.instance.get('amazonqLSP', true)) { - await activateAmazonqLsp(context) -} -``` - -`activateAmazonqLsp` downloads and installs the language-servers bundle then executes the CodeWhisperer start up script (we should find the specific name and path) and initializes the LSP server, including auth set up. - -## aws-toolkit-vscode/packages/core/src/auth/activation.ts - -This file appears critical to how the SageMaker auth worked. It is in core however, and not clear whether it is even in the code path for the LSP server or not. We should review this file closely to understand how IAM credentials worked as it should inform us on what needs to change in the amazonq package to support IAM credentials as well. The `sagemaker.parseCookies` code here also seems important in determining whether the SageMaker instance wants to use IAM or SSO, so that should probably be carried over into the amazonq package as well. - -The `Auth.instance.onDidChangeActiveConnection` handler code should be investigated further. It's not clear if it has anything to do with auth to Q or if it's just older "toolkit"-related auth stuff. - -## aws-toolkit-vscode/packages/core/src/auth/utils.ts - -This is a collection of utility functions and many are related to auth/security. However, it appears to be `initializeCredentialsProviderManager` in our code path, called by `aws-toolkit-vscode/packages/core/src/auth/activation.ts` that may be of importance. We should determine if we need this or similar functionality in amazonq package or if this is just a hold-over that updates the old "toolkit" (i.e. non-Amazon Q parts of the extension) stuff. - -## aws-toolkit-vscode/packages/amazonq/src/lsp/client.ts - -1. line ~68 sets `providesBearerToken: true` but doesn't appear to have anything similar for IAM credentials. -2. line ~93 to the end starts auth for LSP using the `AmazonQLspAuth` class. This all appears to be for SSO tokens, nothing for IAM credentials. - -## aws-toolkit-vscode/packages/amazonq/src/lsp/auth.ts - -1. Defines `AmazonQLspAuth` class that is only for SSO tokens, nothing about IAM credentials. -2. Some SSO token related functions are exported, but nothing similar for IAM credentials. - -## aws-toolkit-vscode/packages/core/src/codewhisperer/activation.ts - -`activate` in the old "core" Q implementation is called by `aws-toolkit-vscode/packages/amazonq/src/extension.ts` line ~121. - -Suspcious code that is still running in `activate` function. How does this not interfer with the new auth code in the amazonq package? - -```typescript -// initialize AuthUtil earlier to make sure it can listen to connection change events. -const auth = AuthUtil.instance -auth.initCodeWhispererHooks() -``` - -Further down in this file it still creates and uses `onst client = new codewhispererClient.DefaultCodeWhispererClient()` which makes it appear to be using both direct calls from the extension as well as the LSP to access the Q service. This bears further investigation into what this code is actually doing. - -## aws-toolkit-vscode/packages/core/src/codewhisperer/client/codewhisperer.ts - -This is the old "core" CodeWhisperer service client. There is likely important code here that informs how IAM authentication works with the service client that may be missing in the language-servers CodeWhisperer client. If my hunch is correct in that the "core" code is still in use for what hasn't been migrated yet, this code may not be actively used for Q Chat which was migrated (see the Experiments flags defaulting to true in the breaking commit) to the amazonq package and should be using the auth there and in language-servers. - -## aws-toolkit-vscode/packages/amazonq/src/extensionNode.ts - -The code below is one of 3 places where the 'amazonqChatLSP' experiment is set to on by default in the commit that broke SageMaker auth. There is some "auth"-related code in this file that should be investigated further to determine if it has any impact on the broken SageMaker auth. It isn't obvious that it does or doesn't. It may just be used in the MynahUI Q Chat webview, and not the LSP server. - -```typescript -if (!Experiments.instance.get('amazonqChatLSP', true)) { -``` - -## aws-toolkit-vscode/packages/core/src/auth/auth.ts - -This file was updated recently for the SMUS project. It may not be directly related to the broken SageMaker auth issue, but the comments on the added/changed functions are suspicious regarding how credentials are received. SMUS may be adding a different way to get IAM credentials than what SMAI used. - -```typescript -/** - * Returns true if credentials are provided by the environment (ex. via ~/.aws/) - * - * @param isC9 boolean for if Cloud9 is host - * @param isSM boolean for if SageMaker is host - * @returns boolean for if C9 "OR" SM - */ -export function hasVendedIamCredentials(isC9?: boolean, isSM?: boolean) { - isC9 ??= isCloud9() - isSM ??= isSageMaker() - return isSM || isC9 -} - -/** - * Returns true if credentials are provided by the metadata files in environment (ex. for IAM via ~/.aws/ and in a future case with SSO, from /cache or /sso) - * @param isSMUS boolean if SageMaker Unified Studio is host - * @returns boolean if SMUS - */ -export function hasVendedCredentialsFromMetadata(isSMUS?: boolean) { - isSMUS ??= isSageMaker('SMUS') - return isSMUS -} -``` - -There is also A LOT of other auth related functionality here, but it's in "core" and may not be directly related the code paths for LSP and breaking auth in SageMaker. - -## aws-toolkit-vscode/packages/core/src/codewhisperer/util/authUtil.ts - -There is some `isSageMaker`-related code here that we should investigate. It appears to be important to auth with SageMaker, but it's not clear if it or similar code is needed and has made it into the amazonq package. Once we confirm any of this code is in our code path of concern, it should be investigated further. - -## aws-toolkit-vscode/packages/amazonq/src/lsp/chat/webviewProvider.ts - -While there is special SageMaker handling in this file, it is not clear if it is related to IAM auth issues with the LSP or it is just related to the chat UI. If we find it is in our code path, we can investigate further. - -# Proposed Fix for SageMaker IAM Authentication in Amazon Q LSP - -> **NOTE:** We should start back tomorrow by addressing the issues and concerns raised in this document first thing, particularly the SageMaker cookie detection and connection metadata handling for IAM authentication. - -## Issue Summary - -The Amazon Q extension for VSCode fails to authenticate in SageMaker environments after v1.62.0 due to a change in architecture. The extension moved from directly calling the Q service using an AWS SDK client to indirectly calling it through the aws-lsp-codewhisperer service (Flare). While the old implementation had specific handling for SageMaker IAM credentials, the new LSP-based implementation only supports SSO token authentication. - -## Root Cause Analysis - -### Breaking Change - -Commit `938bb376647414776a55d7dd7d6761c863764c5c` enabled three experiment flags by default: - -1. `amazonqLSP` in `packages/amazonq/src/extension.ts` (line ~119) - Controls whether to activate the Amazon Q LSP -2. `amazonqChatLSP` in `packages/amazonq/src/extensionNode.ts` (line ~53) - Controls whether to use the legacy chat provider or the LSP-based chat provider -3. `amazonqChatLSP` in `packages/amazonq/src/lsp/client.ts` (line ~117) - Controls whether to activate the chat functionality in the LSP client - -This change moved the extension from using the core implementation to the LSP implementation, which lacks IAM credential support. - -### Recent IAM Support in Language-Servers Repository - -A significant recent commit in the language-servers repository adds IAM authentication support: - -**Commit ID:** 16b287b9e -**Author:** sdharani91 -**Date:** 2025-06-26 -**Title:** feat: enable iam auth for agentic chat (#1736) - -Key changes in this commit: - -1. **Environment Variable Flag**: - - ```typescript - // Added function to check for IAM auth mode - export function isUsingIAMAuth(): boolean { - return process.env.USE_IAM_AUTH === 'true' - } - ``` - -2. **Service Manager Selection**: - - ```typescript - // In qAgenticChatServer.ts - amazonQServiceManager = isUsingIAMAuth() ? getOrThrowBaseIAMServiceManager() : getOrThrowBaseTokenServiceManager() - ``` - -3. **IAM Credentials Handling**: - - ```typescript - // Added function to extract IAM credentials - export function getIAMCredentialsFromProvider(credentialsProvider: CredentialsProvider) { - if (!credentialsProvider.hasCredentials('iam')) { - throw new Error('Missing IAM creds') - } - - const credentials = credentialsProvider.getCredentials('iam') as Credentials - return { - accessKeyId: credentials.accessKeyId, - secretAccessKey: credentials.secretAccessKey, - sessionToken: credentials.sessionToken, - } - } - ``` - -4. **Unified Chat Response Interface**: - - ```typescript - // Created types to handle both auth flows - export type ChatCommandInput = SendMessageCommandInput | GenerateAssistantResponseCommandInputCodeWhispererStreaming - export type ChatCommandOutput = - | SendMessageCommandOutput - | GenerateAssistantResponseCommandOutputCodeWhispererStreaming - ``` - -5. **Source Parameter for IAM**: - ```typescript - // Added source parameter for IAM requests - request.source = 'IDE' - ``` - -This commit shows that IAM authentication support has been added to the language-servers repository, but the extension needs to set the `USE_IAM_AUTH` environment variable to `true` when running in SageMaker environments. - -## Proposed Fix - -Based on our investigation of the language-server-runtimes repository and the previous implementation attempt, here's a refined solution: - -1. **Set Environment Variable for IAM Auth**: - - ```typescript - // In packages/core/src/shared/lsp/utils/platform.ts - const env = { ...process.env } - if (isSageMaker()) { - // Check SageMaker cookie to determine auth mode - try { - const result = await vscode.commands.executeCommand('sagemaker.parseCookies') - if (result?.authMode !== 'Sso') { - env.USE_IAM_AUTH = 'true' - getLogger().info(`[SageMaker Debug] Setting USE_IAM_AUTH=true for language server process`) - } - } catch (err) { - getLogger().error('Failed to parse SageMaker cookies: %O', err) - // Default to IAM auth if cookie parsing fails - env.USE_IAM_AUTH = 'true' - getLogger().info(`[SageMaker Debug] Setting USE_IAM_AUTH=true for language server process (default)`) - } - } - - const lspProcess = new ChildProcess(bin, args, { - warnThresholds, - spawnOptions: { env }, - }) - ``` - -2. **Enhance `AmazonQLspAuth` Class** (`packages/amazonq/src/lsp/auth.ts`): - - ```typescript - async refreshConnection(force: boolean = false) { - const activeConnection = this.authUtil.conn - if (this.authUtil.isConnectionValid()) { - if (isSsoConnection(activeConnection)) { - // Existing SSO path - const token = await this.authUtil.getBearerToken() - await (force ? this._updateBearerToken(token) : this.updateBearerToken(token)) - } else if (isSageMaker() && isIamConnection(activeConnection)) { - // SageMaker IAM path - try { - const credentials = await this.authUtil.getCredentials() - if (credentials && credentials.accessKeyId && credentials.secretAccessKey) { - await (force ? this._updateIamCredentials(credentials) : this.updateIamCredentials(credentials)) - } else { - getLogger().error('Invalid IAM credentials: %O', credentials) - } - } catch (err) { - getLogger().error('Failed to get IAM credentials: %O', err) - } - } - } - } - - public updateIamCredentials = onceChanged(this._updateIamCredentials.bind(this)) - private async _updateIamCredentials(credentials: any) { - try { - // Extract only the required fields to match the expected format - const iamCredentials = { - accessKeyId: credentials.accessKeyId, - secretAccessKey: credentials.secretAccessKey, - sessionToken: credentials.sessionToken, - } - - const request = await this.createUpdateIamCredentialsRequest(iamCredentials) - await this.client.sendRequest(iamCredentialsUpdateRequestType.method, request) - this.client.info(`UpdateIamCredentials: Success`) - } catch (err) { - getLogger().error('Failed to update IAM credentials: %O', err) - } - } - ``` - -3. **Update Connection Metadata Handler** (`packages/amazonq/src/lsp/client.ts`): - - ```typescript - client.onRequest(notificationTypes.getConnectionMetadata.method, () => { - // For IAM auth, provide a default startUrl - if (process.env.USE_IAM_AUTH === 'true') { - return { - sso: { - startUrl: 'https://amzn.awsapps.com/start', // Default for IAM auth - }, - } - } - - // For SSO auth, use the actual startUrl - return { - sso: { - startUrl: AuthUtil.instance.auth.startUrl, - }, - } - }) - ``` - -4. **Modify Client Initialization** (`packages/amazonq/src/lsp/client.ts`): - - ```typescript - const useIamAuth = isSageMaker() && process.env.USE_IAM_AUTH === 'true' - - initializationOptions: { - // ... - credentials: { - providesBearerToken: !useIamAuth, - providesIam: useIamAuth, - }, - } - ``` - -5. **Ensure Auto-login Happens Early** (`packages/amazonq/src/lsp/activation.ts`): - ```typescript - export async function activate(ctx: vscode.ExtensionContext): Promise { - try { - // Check for SageMaker and auto-login if needed - if (isSageMaker()) { - try { - const result = await vscode.commands.executeCommand('sagemaker.parseCookies') - if (result?.authMode !== 'Sso') { - // Auto-login with IAM credentials - const sagemakerProfileId = asString({ - credentialSource: 'ec2', - credentialTypeId: 'sagemaker-instance', - }) - await Auth.instance.tryAutoConnect(sagemakerProfileId) - getLogger().info(`Automatically connected with SageMaker IAM credentials`) - } - } catch (err) { - getLogger().error('Failed to parse SageMaker cookies: %O', err) - } - } - - await lspSetupStage('all', async () => { - const installResult = await new AmazonQLspInstaller().resolve() - await lspSetupStage('launch', async () => await startLanguageServer(ctx, installResult.resourcePaths)) - }) - } catch (err) { - const e = err as ToolkitError - void vscode.window.showInformationMessage(`Unable to launch amazonq language server: ${e.message}`) - } - } - ``` - -This refined solution addresses the issues identified in the previous implementation attempt: - -1. It properly checks the SageMaker cookie to determine the auth mode -2. It ensures the IAM credentials are formatted correctly -3. It adds robust error handling -4. It ensures auto-login happens early in the initialization process - -# Next Steps - -## Plan for SageMaker Environment Testing - -We are going to set up a comprehensive testing environment on the SageMaker instance to debug and fix the IAM authentication issue: - -1. **Repository Setup**: - - - Clone aws-toolkit-vscode repository (already done locally) - - Clone language-servers repository to SageMaker instance - - Configure aws-toolkit-vscode to use local build of language-servers instead of downloaded version - -2. **Development Workflow**: - - - Make changes to language-servers codebase directly on SageMaker instance - - Add comprehensive logging throughout the authentication flow - - Test changes immediately in the SageMaker environment where the issue occurs - - Use `amazonq.trace.server` setting for detailed LSP server logs - -3. **Key Areas to Investigate**: - - - Verify that `USE_IAM_AUTH` environment variable is properly set and inherited - - Confirm IAM credentials are correctly passed from extension to language server - - Validate that language server selects correct service manager based on auth mode - - Test that SageMaker cookie detection works properly - -4. **Debugging Strategy**: - - - Follow the exact code execution path from extension activation to LSP authentication - - Add logging at each critical step to trace the authentication flow - - Capture and analyze any errors or failures in the authentication process - - Compare behavior between working SSO environments and failing SageMaker IAM environment - -5. **Implementation Priority**: - - First implement SageMaker cookie detection to determine auth mode - - Add IAM credential handling to AmazonQLspAuth class - - Ensure proper environment variable setting for language server process - - Test and validate the complete authentication flow - -This approach will allow us to make real-time changes and immediately test them in the actual environment where the authentication failure occurs, giving us the best chance to identify and fix the root cause. - -## Critical Issues to Address First - -The document emphasizes that we should **"start back tomorrow by addressing the issues and concerns raised in this document first thing, particularly the SageMaker cookie detection and connection metadata handling for IAM authentication."** - -The most critical missing pieces are: - -1. **SageMaker cookie detection** to determine when to use IAM vs SSO auth -2. **Connection metadata handling** for IAM authentication -3. **Proper error handling** throughout the authentication flow - -These should be implemented before testing the solution in a SageMaker environment. From 669354bef7d64e0dec5621546b74fb7a9090bb26 Mon Sep 17 00:00:00 2001 From: Dung Dong Date: Wed, 23 Jul 2025 13:25:31 -0700 Subject: [PATCH 51/69] fix(amazonq): update shortcut name to reuse for MCP tools --- aws-toolkit-vscode.code-workspace | 9 +++++++++ packages/amazonq/.vscode/launch.json | 6 +++--- packages/amazonq/package.json | 6 +++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/aws-toolkit-vscode.code-workspace b/aws-toolkit-vscode.code-workspace index f03aafae2fe..66473183814 100644 --- a/aws-toolkit-vscode.code-workspace +++ b/aws-toolkit-vscode.code-workspace @@ -12,6 +12,15 @@ { "path": "packages/amazonq", }, + { + "path": "../language-servers", + }, + { + "path": "../mynah-ui", + }, + { + "path": "../aws-toolkit-common", + }, ], "settings": { "typescript.tsdk": "node_modules/typescript/lib", diff --git a/packages/amazonq/.vscode/launch.json b/packages/amazonq/.vscode/launch.json index b00c5071ce5..cdeabe152a9 100644 --- a/packages/amazonq/.vscode/launch.json +++ b/packages/amazonq/.vscode/launch.json @@ -13,10 +13,10 @@ "args": ["--extensionDevelopmentPath=${workspaceFolder}"], "env": { "SSMDOCUMENT_LANGUAGESERVER_PORT": "6010", - "WEBPACK_DEVELOPER_SERVER": "http://localhost:8080" + "WEBPACK_DEVELOPER_SERVER": "http://localhost:8080", // Below allows for overrides used during development - // "__AMAZONQLSP_PATH": "${workspaceFolder}/../../../language-servers/app/aws-lsp-codewhisperer-runtimes/out/agent-standalone.js", - // "__AMAZONQLSP_UI": "${workspaceFolder}/../../../language-servers/chat-client/build/amazonq-ui.js" + "__AMAZONQLSP_PATH": "${workspaceFolder}/../../../language-servers/app/aws-lsp-codewhisperer-runtimes/out/agent-standalone.js", + "__AMAZONQLSP_UI": "${workspaceFolder}/../../../language-servers/chat-client/build/amazonq-ui.js" }, "envFile": "${workspaceFolder}/.local.env", "outFiles": ["${workspaceFolder}/dist/**/*.js", "${workspaceFolder}/../core/dist/**/*.js"], diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index 9dfc4565f3b..e60da1c50be 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -562,17 +562,17 @@ "commands": [ { "command": "aws.amazonq.stopCmdExecution", - "title": "Stop Amazon Q Command Execution", + "title": "Stop Amazon Q", "category": "%AWS.amazonq.title%" }, { "command": "aws.amazonq.runCmdExecution", - "title": "Run Amazon Q Command Execution", + "title": "Run Amazon Q Tool", "category": "%AWS.amazonq.title%" }, { "command": "aws.amazonq.rejectCmdExecution", - "title": "Reject Amazon Q Command Execution", + "title": "Reject Amazon Q Tool", "category": "%AWS.amazonq.title%" }, { From 95811a6ab5ad8f3ab121696efb727a19383251be Mon Sep 17 00:00:00 2001 From: Dung Dong Date: Wed, 23 Jul 2025 13:26:51 -0700 Subject: [PATCH 52/69] fix: revert dev config --- aws-toolkit-vscode.code-workspace | 9 --------- packages/amazonq/.vscode/launch.json | 6 +++--- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/aws-toolkit-vscode.code-workspace b/aws-toolkit-vscode.code-workspace index 66473183814..f03aafae2fe 100644 --- a/aws-toolkit-vscode.code-workspace +++ b/aws-toolkit-vscode.code-workspace @@ -12,15 +12,6 @@ { "path": "packages/amazonq", }, - { - "path": "../language-servers", - }, - { - "path": "../mynah-ui", - }, - { - "path": "../aws-toolkit-common", - }, ], "settings": { "typescript.tsdk": "node_modules/typescript/lib", diff --git a/packages/amazonq/.vscode/launch.json b/packages/amazonq/.vscode/launch.json index cdeabe152a9..b00c5071ce5 100644 --- a/packages/amazonq/.vscode/launch.json +++ b/packages/amazonq/.vscode/launch.json @@ -13,10 +13,10 @@ "args": ["--extensionDevelopmentPath=${workspaceFolder}"], "env": { "SSMDOCUMENT_LANGUAGESERVER_PORT": "6010", - "WEBPACK_DEVELOPER_SERVER": "http://localhost:8080", + "WEBPACK_DEVELOPER_SERVER": "http://localhost:8080" // Below allows for overrides used during development - "__AMAZONQLSP_PATH": "${workspaceFolder}/../../../language-servers/app/aws-lsp-codewhisperer-runtimes/out/agent-standalone.js", - "__AMAZONQLSP_UI": "${workspaceFolder}/../../../language-servers/chat-client/build/amazonq-ui.js" + // "__AMAZONQLSP_PATH": "${workspaceFolder}/../../../language-servers/app/aws-lsp-codewhisperer-runtimes/out/agent-standalone.js", + // "__AMAZONQLSP_UI": "${workspaceFolder}/../../../language-servers/chat-client/build/amazonq-ui.js" }, "envFile": "${workspaceFolder}/.local.env", "outFiles": ["${workspaceFolder}/dist/**/*.js", "${workspaceFolder}/../core/dist/**/*.js"], From 2093c59abb740ffc250b985da1697213ce3db7d1 Mon Sep 17 00:00:00 2001 From: abhraina-aws Date: Wed, 23 Jul 2025 15:09:45 -0700 Subject: [PATCH 53/69] fix(amazonq): point to the log file inside the folder (#7744) ## Problem Log folder was getting highlighted instead of the file. ## Solution Pointed to the file itself instead of the folder. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/amazonq/src/lsp/chat/messages.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index 71de85f90a7..7b3b130ff85 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -447,13 +447,15 @@ export function registerMessageListeners( ) // Get the log directory path - const logPath = globals.context.logUri?.fsPath + const logFolderPath = globals.context.logUri?.fsPath const result = { ...message.params, success: false } - if (logPath) { + if (logFolderPath) { // Open the log directory in the OS file explorer directly languageClient.info('[VSCode Client] Opening logs directory') - await vscode.commands.executeCommand('revealFileInOS', vscode.Uri.file(logPath)) + const path = require('path') + const logFilePath = path.join(logFolderPath, 'Amazon Q Logs.log') + await vscode.commands.executeCommand('revealFileInOS', vscode.Uri.file(logFilePath)) result.success = true } else { // Fallback: show error if log path is not available From 8748fc31898848276e73c64b41c8eb0567a8df2c Mon Sep 17 00:00:00 2001 From: Aidan Ton Date: Wed, 23 Jul 2025 15:30:51 -0700 Subject: [PATCH 54/69] fix(amazonq): use diffWordsWithSpace instead of diffChars to calculate highlightedRanges --- .../app/inline/EditRendering/svgGenerator.ts | 52 ++----------------- .../inline/EditRendering/svgGenerator.test.ts | 28 +--------- 2 files changed, 6 insertions(+), 74 deletions(-) diff --git a/packages/amazonq/src/app/inline/EditRendering/svgGenerator.ts b/packages/amazonq/src/app/inline/EditRendering/svgGenerator.ts index b9cf33e255d..178045afaee 100644 --- a/packages/amazonq/src/app/inline/EditRendering/svgGenerator.ts +++ b/packages/amazonq/src/app/inline/EditRendering/svgGenerator.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { diffChars } from 'diff' +import { diffWordsWithSpace } from 'diff' import * as vscode from 'vscode' import { ToolkitError, getLogger } from 'aws-core-vscode/shared' import { diffUtilities } from 'aws-core-vscode/shared' @@ -413,45 +413,6 @@ export class SvgGenerationService { const originalRanges: Range[] = [] const afterRanges: Range[] = [] - /** - * Merges ranges on the same line that are separated by only one character - */ - const mergeAdjacentRanges = (ranges: Range[]): Range[] => { - const sortedRanges = [...ranges].sort((a, b) => { - if (a.line !== b.line) { - return a.line - b.line - } - return a.start - b.start - }) - - const result: Range[] = [] - - // Process all ranges - for (let i = 0; i < sortedRanges.length; i++) { - const current = sortedRanges[i] - - // If this is the last range or ranges are on different lines, add it directly - if (i === sortedRanges.length - 1 || current.line !== sortedRanges[i + 1].line) { - result.push(current) - continue - } - - // Check if current range and next range can be merged - const next = sortedRanges[i + 1] - if (current.line === next.line && next.start - current.end <= 1) { - sortedRanges[i + 1] = { - line: current.line, - start: current.start, - end: Math.max(current.end, next.end), - } - } else { - result.push(current) - } - } - - return result - } - // Create reverse mapping for quicker lookups const reverseMap = new Map() for (const [original, modified] of modifiedLines.entries()) { @@ -465,7 +426,7 @@ export class SvgGenerationService { // If line exists in modifiedLines as a key, process character diffs if (Array.from(modifiedLines.keys()).includes(line)) { const modifiedLine = modifiedLines.get(line)! - const changes = diffChars(line, modifiedLine) + const changes = diffWordsWithSpace(line, modifiedLine) let charPos = 0 for (const part of changes) { @@ -497,7 +458,7 @@ export class SvgGenerationService { if (reverseMap.has(line)) { const originalLine = reverseMap.get(line)! - const changes = diffChars(originalLine, line) + const changes = diffWordsWithSpace(originalLine, line) let charPos = 0 for (const part of changes) { @@ -522,12 +483,9 @@ export class SvgGenerationService { } } - const mergedOriginalRanges = mergeAdjacentRanges(originalRanges) - const mergedAfterRanges = mergeAdjacentRanges(afterRanges) - return { - removedRanges: mergedOriginalRanges, - addedRanges: mergedAfterRanges, + removedRanges: originalRanges, + addedRanges: afterRanges, } } } diff --git a/packages/amazonq/test/unit/app/inline/EditRendering/svgGenerator.test.ts b/packages/amazonq/test/unit/app/inline/EditRendering/svgGenerator.test.ts index 81ba05251e2..657ff5c2915 100644 --- a/packages/amazonq/test/unit/app/inline/EditRendering/svgGenerator.test.ts +++ b/packages/amazonq/test/unit/app/inline/EditRendering/svgGenerator.test.ts @@ -150,7 +150,7 @@ describe('SvgGenerationService', function () { }) describe('highlight ranges', function () { - it('should generate highlight ranges for character-level changes', function () { + it('should generate highlight ranges for word-level changes', function () { const originalCode = ['function test() {', ' return 42;', '}'] const afterCode = ['function test() {', ' return 100;', '}'] const modifiedLines = new Map([[' return 42;', ' return 100;']]) @@ -174,32 +174,6 @@ describe('SvgGenerationService', function () { assert.ok(addedRange.end > addedRange.start) }) - it('should merge adjacent highlight ranges', function () { - const originalCode = ['function test() {', ' return 42;', '}'] - const afterCode = ['function test() {', ' return 100;', '}'] - const modifiedLines = new Map([[' return 42;', ' return 100;']]) - - const generateHighlightRanges = (service as any).generateHighlightRanges.bind(service) - const result = generateHighlightRanges(originalCode, afterCode, modifiedLines) - - // Adjacent ranges should be merged - const sortedRanges = [...result.addedRanges].sort((a, b) => { - if (a.line !== b.line) { - return a.line - b.line - } - return a.start - b.start - }) - - // Check that no adjacent ranges exist - for (let i = 0; i < sortedRanges.length - 1; i++) { - const current = sortedRanges[i] - const next = sortedRanges[i + 1] - if (current.line === next.line) { - assert.ok(next.start - current.end > 1, 'Adjacent ranges should be merged') - } - } - }) - it('should handle HTML escaping in highlight edits', function () { const newLines = ['function test() {', ' return "";', '}'] const highlightRanges = [{ line: 1, start: 10, end: 35 }] From 9128f47cc67a9f0fa7eb48d3dd8ae4acf4339286 Mon Sep 17 00:00:00 2001 From: abhraina-aws Date: Wed, 23 Jul 2025 17:04:58 -0700 Subject: [PATCH 55/69] feat(amazonq): added show logs to the top menu bar dropdown (#7745) ## Problem Needed to add a new drop down show logs button at the top. Having just a web view button would lead to this functionality not work incase the LS doesnt work. ## Solution Added the same show logs functionality in the top drop down. image --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/amazonq/package.json | 11 ++++++++ packages/core/package.nls.json | 1 + packages/core/src/codewhisperer/activation.ts | 2 ++ .../codewhisperer/commands/basicCommands.ts | 26 +++++++++++++++++++ 4 files changed, 40 insertions(+) diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index e60da1c50be..86b2f45f41b 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -408,6 +408,11 @@ "when": "(view == aws.amazonq.AmazonQChatView) && aws.codewhisperer.connected && !aws.isSageMakerUnifiedStudio", "group": "2_amazonQ@4" }, + { + "command": "aws.amazonq.showLogs", + "when": "view == aws.amazonq.AmazonQChatView", + "group": "1_amazonQ@5" + }, { "command": "aws.amazonq.reconnect", "when": "(view == aws.amazonq.AmazonQChatView) && aws.codewhisperer.connectionExpired", @@ -636,6 +641,12 @@ "category": "%AWS.amazonq.title%", "enablement": "aws.codewhisperer.connected" }, + { + "command": "aws.amazonq.showLogs", + "title": "%AWS.command.codewhisperer.showLogs%", + "category": "%AWS.amazonq.title%", + "enablement": "aws.codewhisperer.connected" + }, { "command": "aws.amazonq.selectRegionProfile", "title": "Change Profile", diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index 20d500e07bd..0a25550ec22 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -275,6 +275,7 @@ "AWS.command.codewhisperer.signout": "Sign Out", "AWS.command.codewhisperer.reconnect": "Reconnect", "AWS.command.codewhisperer.openReferencePanel": "Open Code Reference Log", + "AWS.command.codewhisperer.showLogs": "Show Logs", "AWS.command.q.selectRegionProfile": "Select Profile", "AWS.command.q.transform.acceptChanges": "Accept", "AWS.command.q.transform.rejectChanges": "Reject", diff --git a/packages/core/src/codewhisperer/activation.ts b/packages/core/src/codewhisperer/activation.ts index d6dd7fdc61d..1e73b640a1e 100644 --- a/packages/core/src/codewhisperer/activation.ts +++ b/packages/core/src/codewhisperer/activation.ts @@ -23,6 +23,7 @@ import { enableCodeSuggestions, toggleCodeSuggestions, showReferenceLog, + showLogs, showSecurityScan, showLearnMore, showSsoSignIn, @@ -299,6 +300,7 @@ export async function activate(context: ExtContext): Promise { ), vscode.window.registerWebviewViewProvider(ReferenceLogViewProvider.viewType, ReferenceLogViewProvider.instance), showReferenceLog.register(), + showLogs.register(), showExploreAgentsView.register(), vscode.languages.registerCodeLensProvider( [...CodeWhispererConstants.platformLanguageIds], diff --git a/packages/core/src/codewhisperer/commands/basicCommands.ts b/packages/core/src/codewhisperer/commands/basicCommands.ts index efe993356bd..a8c21b86ce2 100644 --- a/packages/core/src/codewhisperer/commands/basicCommands.ts +++ b/packages/core/src/codewhisperer/commands/basicCommands.ts @@ -147,6 +147,32 @@ export const showReferenceLog = Commands.declare( } ) +export const showLogs = Commands.declare( + { id: 'aws.amazonq.showLogs', compositeKey: { 1: 'source' } }, + () => async (_: VsCodeCommandArg, source: CodeWhispererSource) => { + if (_ !== placeholder) { + source = 'ellipsesMenu' + } + + // Show warning message without buttons - just informational + void vscode.window.showWarningMessage( + 'Log files may contain sensitive information such as account IDs, resource names, and other data. Be careful when sharing these logs.' + ) + + // Get the log directory path + const logFolderPath = globals.context.logUri?.fsPath + const path = require('path') + const logFilePath = path.join(logFolderPath, 'Amazon Q Logs.log') + if (logFilePath) { + // Open the log directory in the OS file explorer directly + await vscode.commands.executeCommand('revealFileInOS', vscode.Uri.file(logFilePath)) + } else { + // Fallback: show error if log path is not available + void vscode.window.showErrorMessage('Log location not available.') + } + } +) + export const showExploreAgentsView = Commands.declare( { id: 'aws.amazonq.exploreAgents', compositeKey: { 1: 'source' } }, () => async (_: VsCodeCommandArg, source: CodeWhispererSource) => { From 2d9440ab85b26b21a8dbd321df0bcc61aa66d5e6 Mon Sep 17 00:00:00 2001 From: Laxman Reddy <141967714+laileni-aws@users.noreply.github.com> Date: Thu, 24 Jul 2025 10:10:42 -0700 Subject: [PATCH 56/69] refactor(amazonq): Removing unwanted / agents code (#7735) ## Problem - There is lot of duplicate and unwanted redundant code in [aws-toolkit-vscode](https://github.com/aws/aws-toolkit-vscode) repository. ## Solution - This is the first PR to remove unwanted code. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- .../src/app/chat/node/activateAgents.ts | 3 - packages/amazonq/test/e2e/amazonq/doc.test.ts | 492 ------ .../test/e2e/amazonq/featureDev.test.ts | 345 ---- .../amazonq/test/e2e/amazonq/testGen.test.ts | 209 --- packages/core/src/amazonq/indexNode.ts | 5 +- .../ui/apps/amazonqCommonsConnector.ts | 17 +- .../webview/ui/apps/docChatConnector.ts | 226 --- .../ui/apps/featureDevChatConnector.ts | 212 --- .../webview/ui/apps/testChatConnector.ts | 293 ---- .../core/src/amazonq/webview/ui/connector.ts | 139 -- .../amazonq/webview/ui/connectorAdapter.ts | 11 +- .../amazonq/webview/ui/followUps/generator.ts | 26 - .../amazonq/webview/ui/followUps/handler.ts | 80 +- packages/core/src/amazonq/webview/ui/main.ts | 43 +- .../amazonq/webview/ui/messages/controller.ts | 6 - .../webview/ui/quickActions/generator.ts | 27 +- .../webview/ui/quickActions/handler.ts | 157 +- .../webview/ui/storages/tabsStorage.ts | 18 +- .../src/amazonq/webview/ui/tabs/constants.ts | 25 - .../src/amazonq/webview/ui/tabs/generator.ts | 6 - packages/core/src/amazonqDoc/app.ts | 3 - packages/core/src/amazonqFeatureDev/app.ts | 6 - packages/core/src/amazonqTest/app.ts | 76 - .../amazonqTest/chat/controller/controller.ts | 1464 ----------------- .../chat/controller/messenger/messenger.ts | 365 ---- .../controller/messenger/messengerUtils.ts | 31 - .../src/amazonqTest/chat/session/session.ts | 77 - .../amazonqTest/chat/storages/chatSession.ts | 61 - .../chat/views/actions/uiMessageListener.ts | 161 -- .../chat/views/connector/connector.ts | 256 --- packages/core/src/amazonqTest/error.ts | 67 - packages/core/src/amazonqTest/index.ts | 6 - .../core/src/amazonqTest/models/constants.ts | 147 -- .../commands/startTestGeneration.ts | 259 --- .../core/src/codewhisperer/models/model.ts | 50 - .../service/securityScanHandler.ts | 9 +- .../codewhisperer/service/testGenHandler.ts | 326 ---- .../src/codewhisperer/util/telemetryHelper.ts | 50 - .../core/src/codewhisperer/util/zipUtil.ts | 53 +- packages/core/src/shared/db/chatDb/util.ts | 6 - .../core/src/shared/filesystemUtilities.ts | 2 - .../src/test/codewhisperer/zipUtil.test.ts | 42 - 42 files changed, 16 insertions(+), 5841 deletions(-) delete mode 100644 packages/amazonq/test/e2e/amazonq/doc.test.ts delete mode 100644 packages/amazonq/test/e2e/amazonq/featureDev.test.ts delete mode 100644 packages/amazonq/test/e2e/amazonq/testGen.test.ts delete mode 100644 packages/core/src/amazonq/webview/ui/apps/docChatConnector.ts delete mode 100644 packages/core/src/amazonq/webview/ui/apps/featureDevChatConnector.ts delete mode 100644 packages/core/src/amazonq/webview/ui/apps/testChatConnector.ts delete mode 100644 packages/core/src/amazonqTest/app.ts delete mode 100644 packages/core/src/amazonqTest/chat/controller/controller.ts delete mode 100644 packages/core/src/amazonqTest/chat/controller/messenger/messenger.ts delete mode 100644 packages/core/src/amazonqTest/chat/controller/messenger/messengerUtils.ts delete mode 100644 packages/core/src/amazonqTest/chat/session/session.ts delete mode 100644 packages/core/src/amazonqTest/chat/storages/chatSession.ts delete mode 100644 packages/core/src/amazonqTest/chat/views/actions/uiMessageListener.ts delete mode 100644 packages/core/src/amazonqTest/chat/views/connector/connector.ts delete mode 100644 packages/core/src/amazonqTest/error.ts delete mode 100644 packages/core/src/amazonqTest/index.ts delete mode 100644 packages/core/src/amazonqTest/models/constants.ts delete mode 100644 packages/core/src/codewhisperer/commands/startTestGeneration.ts delete mode 100644 packages/core/src/codewhisperer/service/testGenHandler.ts diff --git a/packages/amazonq/src/app/chat/node/activateAgents.ts b/packages/amazonq/src/app/chat/node/activateAgents.ts index 954f2892eda..cd0309d7f2d 100644 --- a/packages/amazonq/src/app/chat/node/activateAgents.ts +++ b/packages/amazonq/src/app/chat/node/activateAgents.ts @@ -11,9 +11,6 @@ export function activateAgents() { const appInitContext = DefaultAmazonQAppInitContext.instance amazonqNode.cwChatAppInit(appInitContext) - amazonqNode.featureDevChatAppInit(appInitContext) amazonqNode.gumbyChatAppInit(appInitContext) - amazonqNode.testChatAppInit(appInitContext) - amazonqNode.docChatAppInit(appInitContext) scanChatAppInit(appInitContext) } diff --git a/packages/amazonq/test/e2e/amazonq/doc.test.ts b/packages/amazonq/test/e2e/amazonq/doc.test.ts deleted file mode 100644 index 20d281fe7b8..00000000000 --- a/packages/amazonq/test/e2e/amazonq/doc.test.ts +++ /dev/null @@ -1,492 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import vscode from 'vscode' -import assert from 'assert' -import { qTestingFramework } from './framework/framework' -import { getTestWindow, registerAuthHook, toTextEditor, using } from 'aws-core-vscode/test' -import { loginToIdC } from './utils/setup' -import { Messenger } from './framework/messenger' -import { FollowUpTypes } from 'aws-core-vscode/amazonq' -import { fs, i18n, sleep } from 'aws-core-vscode/shared' -import { - docGenerationProgressMessage, - DocGenerationStep, - docGenerationSuccessMessage, - docRejectConfirmation, - Mode, -} from 'aws-core-vscode/amazonqDoc' - -describe('Amazon Q Doc Generation', async function () { - let framework: qTestingFramework - let tab: Messenger - let workspaceUri: vscode.Uri - let rootReadmeFileUri: vscode.Uri - - type testProjectConfig = { - path: string - language: string - mockFile: string - mockContent: string - } - const testProjects: testProjectConfig[] = [ - { - path: 'ts-plain-sam-app', - language: 'TypeScript', - mockFile: 'bubbleSort.ts', - mockContent: ` - function bubbleSort(arr: number[]): number[] { - const n = arr.length; - for (let i = 0; i < n - 1; i++) { - for (let j = 0; j < n - i - 1; j++) { - if (arr[j] > arr[j + 1]) { - [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; - } - } - } - return arr; - }`, - }, - { - path: 'ruby-plain-sam-app', - language: 'Ruby', - mockFile: 'bubble_sort.rb', - mockContent: ` - def bubble_sort(arr) - n = arr.length - (n-1).times do |i| - (0..n-i-2).each do |j| - if arr[j] > arr[j+1] - arr[j], arr[j+1] = arr[j+1], arr[j] - end - end - end - arr - end`, - }, - { - path: 'js-plain-sam-app', - language: 'JavaScript', - mockFile: 'bubbleSort.js', - mockContent: ` - function bubbleSort(arr) { - const n = arr.length; - for (let i = 0; i < n - 1; i++) { - for (let j = 0; j < n - i - 1; j++) { - if (arr[j] > arr[j + 1]) { - [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; - } - } - } - return arr; - }`, - }, - { - path: 'java11-plain-maven-sam-app', - language: 'Java', - mockFile: 'BubbleSort.java', - mockContent: ` - public static void bubbleSort(int[] arr) { - int n = arr.length; - for (int i = 0; i < n - 1; i++) { - for (int j = 0; j < n - i - 1; j++) { - if (arr[j] > arr[j + 1]) { - int temp = arr[j]; - arr[j] = arr[j + 1]; - arr[j + 1] = temp; - } - } - } - }`, - }, - { - path: 'go1-plain-sam-app', - language: 'Go', - mockFile: 'bubble_sort.go', - mockContent: ` - func bubbleSort(arr []int) []int { - n := len(arr) - for i := 0; i < n-1; i++ { - for j := 0; j < n-i-1; j++ { - if arr[j] > arr[j+1] { - arr[j], arr[j+1] = arr[j+1], arr[j] - } - } - } - return arr - }`, - }, - { - path: 'python3.7-plain-sam-app', - language: 'Python', - mockFile: 'bubble_sort.py', - mockContent: ` - def bubble_sort(arr): - n = len(arr) - for i in range(n-1): - for j in range(0, n-i-1): - if arr[j] > arr[j+1]: - arr[j], arr[j+1] = arr[j+1], arr[j] - return arr`, - }, - ] - - const docUtils = { - async initializeDocOperation(operation: 'create' | 'update' | 'edit') { - console.log(`Initializing documentation ${operation} operation`) - - switch (operation) { - case 'create': - await tab.waitForButtons([FollowUpTypes.CreateDocumentation, FollowUpTypes.UpdateDocumentation]) - tab.clickButton(FollowUpTypes.CreateDocumentation) - await tab.waitForText(i18n('AWS.amazonq.doc.answer.createReadme')) - break - case 'update': - await tab.waitForButtons([FollowUpTypes.CreateDocumentation, FollowUpTypes.UpdateDocumentation]) - tab.clickButton(FollowUpTypes.UpdateDocumentation) - await tab.waitForButtons([FollowUpTypes.SynchronizeDocumentation, FollowUpTypes.EditDocumentation]) - tab.clickButton(FollowUpTypes.SynchronizeDocumentation) - await tab.waitForText(i18n('AWS.amazonq.doc.answer.updateReadme')) - break - case 'edit': - await tab.waitForButtons([FollowUpTypes.UpdateDocumentation]) - tab.clickButton(FollowUpTypes.UpdateDocumentation) - await tab.waitForButtons([FollowUpTypes.SynchronizeDocumentation, FollowUpTypes.EditDocumentation]) - tab.clickButton(FollowUpTypes.EditDocumentation) - await tab.waitForText(i18n('AWS.amazonq.doc.answer.updateReadme')) - break - } - }, - - async handleFolderSelection(testProject: testProjectConfig) { - console.table({ - 'Test in project': { - Path: testProject.path, - Language: testProject.language, - }, - }) - - const projectUri = vscode.Uri.joinPath(workspaceUri, testProject.path) - const readmeFileUri = vscode.Uri.joinPath(projectUri, 'README.md') - - // Cleanup existing README - await fs.delete(readmeFileUri, { force: true }) - - await tab.waitForButtons([FollowUpTypes.ProceedFolderSelection, FollowUpTypes.ChooseFolder]) - tab.clickButton(FollowUpTypes.ChooseFolder) - getTestWindow().onDidShowDialog((d) => d.selectItem(projectUri)) - - return readmeFileUri - }, - - async executeDocumentationFlow(operation: 'create' | 'update' | 'edit', msg?: string) { - const mode = { - create: Mode.CREATE, - update: Mode.SYNC, - edit: Mode.EDIT, - }[operation] - - console.log(`Executing documentation ${operation} flow`) - - await tab.waitForButtons([FollowUpTypes.ProceedFolderSelection]) - tab.clickButton(FollowUpTypes.ProceedFolderSelection) - - if (mode === Mode.EDIT && msg) { - tab.addChatMessage({ prompt: msg }) - } - await tab.waitForText(docGenerationProgressMessage(DocGenerationStep.SUMMARIZING_FILES, mode)) - await tab.waitForText(`${docGenerationSuccessMessage(mode)} ${i18n('AWS.amazonq.doc.answer.codeResult')}`) - await tab.waitForButtons([ - FollowUpTypes.AcceptChanges, - FollowUpTypes.MakeChanges, - FollowUpTypes.RejectChanges, - ]) - }, - - async verifyResult(action: FollowUpTypes, readmeFileUri?: vscode.Uri, shouldExist = true) { - tab.clickButton(action) - - if (action === FollowUpTypes.RejectChanges) { - await tab.waitForText(docRejectConfirmation) - assert.deepStrictEqual(tab.getChatItems().pop()?.body, docRejectConfirmation) - } - await tab.waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession]) - - if (readmeFileUri) { - const fileExists = await fs.exists(readmeFileUri) - console.log(`README file exists: ${fileExists}, Expected: ${shouldExist}`) - assert.strictEqual( - fileExists, - shouldExist, - shouldExist - ? 'README file was not saved to the appropriate folder' - : 'README file should not be saved to the folder' - ) - if (fileExists) { - await fs.delete(readmeFileUri, { force: true }) - } - } - }, - - async prepareMockFile(testProject: testProjectConfig) { - const folderUri = vscode.Uri.joinPath(workspaceUri, testProject.path) - const mockFileUri = vscode.Uri.joinPath(folderUri, testProject.mockFile) - await toTextEditor(testProject.mockContent, testProject.mockFile, folderUri.path) - return mockFileUri - }, - - getRandomTestProject() { - const randomIndex = Math.floor(Math.random() * testProjects.length) - return testProjects[randomIndex] - }, - async setupTest() { - tab = framework.createTab() - tab.addChatMessage({ command: '/doc' }) - tab = framework.getSelectedTab() - await tab.waitForChatFinishesLoading() - }, - } - /** - * Executes a test method with automatic retry capability for retryable errors. - * Uses Promise.race to detect errors during test execution without hanging. - */ - async function retryIfRequired(testMethod: () => Promise, maxAttempts: number = 3) { - const errorMessages = { - tooManyRequests: 'Too many requests', - unexpectedError: 'Encountered an unexpected error when processing the request', - } - const hasRetryableError = () => { - const lastTwoMessages = tab - .getChatItems() - .slice(-2) - .map((item) => item.body) - return lastTwoMessages.some( - (body) => body?.includes(errorMessages.unexpectedError) || body?.includes(errorMessages.tooManyRequests) - ) - } - for (let attempt = 1; attempt <= maxAttempts; attempt++) { - console.log(`Attempt ${attempt}/${maxAttempts}`) - const errorDetectionPromise = new Promise((_, reject) => { - const errorCheckInterval = setInterval(() => { - if (hasRetryableError()) { - clearInterval(errorCheckInterval) - reject(new Error('Retryable error detected')) - } - }, 1000) - }) - try { - await Promise.race([testMethod(), errorDetectionPromise]) - return - } catch (error) { - if (attempt === maxAttempts) { - assert.fail(`Test failed after ${maxAttempts} attempts`) - } - console.log(`Attempt ${attempt} failed, retrying...`) - await sleep(1000 * attempt) - await docUtils.setupTest() - } - } - } - before(async function () { - /** - * The tests are getting throttled, only run them on stable for now - * - * TODO: Re-enable for all versions once the backend can handle them - */ - - const testVersion = process.env['VSCODE_TEST_VERSION'] - if (testVersion && testVersion !== 'stable') { - this.skip() - } - - await using(registerAuthHook('amazonq-test-account'), async () => { - await loginToIdC() - }) - }) - - beforeEach(() => { - registerAuthHook('amazonq-test-account') - framework = new qTestingFramework('doc', true, []) - tab = framework.createTab() - const wsFolders = vscode.workspace.workspaceFolders - if (!wsFolders?.length) { - assert.fail('Workspace folder not found') - } - workspaceUri = wsFolders[0].uri - rootReadmeFileUri = vscode.Uri.joinPath(workspaceUri, 'README.md') - }) - - afterEach(() => { - framework.removeTab(tab.tabID) - framework.dispose() - }) - - describe('Quick action availability', () => { - it('Shows /doc command when doc generation is enabled', async () => { - const command = tab.findCommand('/doc') - if (!command.length) { - assert.fail('Could not find command') - } - - if (command.length > 1) { - assert.fail('Found too many commands with the name /doc') - } - }) - - it('Hide /doc command when doc generation is NOT enabled', () => { - // The beforeEach registers a framework which accepts requests. If we don't dispose before building a new one we have duplicate messages - framework.dispose() - framework = new qTestingFramework('doc', false, []) - const tab = framework.createTab() - const command = tab.findCommand('/doc') - if (command.length > 0) { - assert.fail('Found command when it should not have been found') - } - }) - }) - - describe('/doc entry', () => { - beforeEach(async function () { - await docUtils.setupTest() - }) - - it('Display create and update options on initial load', async () => { - await tab.waitForButtons([FollowUpTypes.CreateDocumentation, FollowUpTypes.UpdateDocumentation]) - }) - it('Return to the select create or update documentation state when cancel button clicked', async () => { - await tab.waitForButtons([FollowUpTypes.CreateDocumentation, FollowUpTypes.UpdateDocumentation]) - tab.clickButton(FollowUpTypes.UpdateDocumentation) - await tab.waitForButtons([FollowUpTypes.SynchronizeDocumentation, FollowUpTypes.EditDocumentation]) - tab.clickButton(FollowUpTypes.SynchronizeDocumentation) - await tab.waitForButtons([ - FollowUpTypes.ProceedFolderSelection, - FollowUpTypes.ChooseFolder, - FollowUpTypes.CancelFolderSelection, - ]) - tab.clickButton(FollowUpTypes.CancelFolderSelection) - await tab.waitForChatFinishesLoading() - const followupButton = tab.getFollowUpButton(FollowUpTypes.CreateDocumentation) - if (!followupButton) { - assert.fail('Could not find follow up button for create or update readme') - } - }) - }) - - describe('README Creation', () => { - let testProject: testProjectConfig - beforeEach(async function () { - await docUtils.setupTest() - testProject = docUtils.getRandomTestProject() - }) - - it('Create and save README in root folder when accepted', async () => { - await retryIfRequired(async () => { - await docUtils.initializeDocOperation('create') - await docUtils.executeDocumentationFlow('create') - await docUtils.verifyResult(FollowUpTypes.AcceptChanges, rootReadmeFileUri, true) - }) - }) - it('Create and save README in subfolder when accepted', async () => { - await retryIfRequired(async () => { - await docUtils.initializeDocOperation('create') - const readmeFileUri = await docUtils.handleFolderSelection(testProject) - await docUtils.executeDocumentationFlow('create') - await docUtils.verifyResult(FollowUpTypes.AcceptChanges, readmeFileUri, true) - }) - }) - - it('Discard README in subfolder when rejected', async () => { - await retryIfRequired(async () => { - await docUtils.initializeDocOperation('create') - const readmeFileUri = await docUtils.handleFolderSelection(testProject) - await docUtils.executeDocumentationFlow('create') - await docUtils.verifyResult(FollowUpTypes.RejectChanges, readmeFileUri, false) - }) - }) - }) - - describe('README Editing', () => { - beforeEach(async function () { - await docUtils.setupTest() - }) - - it('Apply specific content changes when requested', async () => { - await retryIfRequired(async () => { - await docUtils.initializeDocOperation('edit') - await docUtils.executeDocumentationFlow('edit', 'remove the repository structure section') - await docUtils.verifyResult(FollowUpTypes.AcceptChanges, rootReadmeFileUri, true) - }) - }) - - it('Handle unrelated prompts with appropriate error message', async () => { - await retryIfRequired(async () => { - await docUtils.initializeDocOperation('edit') - await tab.waitForButtons([FollowUpTypes.ProceedFolderSelection]) - tab.clickButton(FollowUpTypes.ProceedFolderSelection) - tab.addChatMessage({ prompt: 'tell me about the weather' }) - await tab.waitForEvent(() => - tab - .getChatItems() - .some(({ body }) => body?.startsWith(i18n('AWS.amazonq.doc.error.promptUnrelated'))) - ) - await tab.waitForEvent(() => { - const store = tab.getStore() - return ( - !store.promptInputDisabledState && - store.promptInputPlaceholder === i18n('AWS.amazonq.doc.placeholder.editReadme') - ) - }) - }) - }) - }) - describe('README Updates', () => { - let testProject: testProjectConfig - let mockFileUri: vscode.Uri - - beforeEach(async function () { - await docUtils.setupTest() - testProject = docUtils.getRandomTestProject() - }) - afterEach(async function () { - // Clean up mock file - if (mockFileUri) { - await fs.delete(mockFileUri, { force: true }) - } - }) - - it('Update README with code change in subfolder', async () => { - mockFileUri = await docUtils.prepareMockFile(testProject) - await retryIfRequired(async () => { - await docUtils.initializeDocOperation('update') - const readmeFileUri = await docUtils.handleFolderSelection(testProject) - await docUtils.executeDocumentationFlow('update') - await docUtils.verifyResult(FollowUpTypes.AcceptChanges, readmeFileUri, true) - }) - }) - it('Update root README and incorporate additional changes', async () => { - // Cleanup any existing README - await fs.delete(rootReadmeFileUri, { force: true }) - mockFileUri = await docUtils.prepareMockFile(testProject) - await retryIfRequired(async () => { - await docUtils.initializeDocOperation('update') - await docUtils.executeDocumentationFlow('update') - tab.clickButton(FollowUpTypes.MakeChanges) - tab.addChatMessage({ prompt: 'remove the repository structure section' }) - - await tab.waitForText(docGenerationProgressMessage(DocGenerationStep.SUMMARIZING_FILES, Mode.SYNC)) - await tab.waitForText( - `${docGenerationSuccessMessage(Mode.SYNC)} ${i18n('AWS.amazonq.doc.answer.codeResult')}` - ) - await tab.waitForButtons([ - FollowUpTypes.AcceptChanges, - FollowUpTypes.MakeChanges, - FollowUpTypes.RejectChanges, - ]) - - await docUtils.verifyResult(FollowUpTypes.AcceptChanges, rootReadmeFileUri, true) - }) - }) - }) -}) diff --git a/packages/amazonq/test/e2e/amazonq/featureDev.test.ts b/packages/amazonq/test/e2e/amazonq/featureDev.test.ts deleted file mode 100644 index 87099e2a2d0..00000000000 --- a/packages/amazonq/test/e2e/amazonq/featureDev.test.ts +++ /dev/null @@ -1,345 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import assert from 'assert' -import { qTestingFramework } from './framework/framework' -import sinon from 'sinon' -import { registerAuthHook, using } from 'aws-core-vscode/test' -import { loginToIdC } from './utils/setup' -import { Messenger } from './framework/messenger' -import { FollowUpTypes } from 'aws-core-vscode/amazonq' -import { sleep } from 'aws-core-vscode/shared' - -describe('Amazon Q Feature Dev', function () { - let framework: qTestingFramework - let tab: Messenger - - const prompt = 'Add current timestamp into blank.txt' - const iteratePrompt = `Add a new section in readme to explain your change` - const fileLevelAcceptPrompt = `${prompt} and ${iteratePrompt}` - const informationCard = - 'After you provide a task, I will:\n1. Generate code based on your description and the code in your workspace\n2. Provide a list of suggestions for you to review and add to your workspace\n3. If needed, iterate based on your feedback\nTo learn more, visit the [user guide](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/software-dev.html)' - const tooManyRequestsWaitTime = 100000 - - async function waitForText(text: string) { - await tab.waitForText(text, { - waitIntervalInMs: 250, - waitTimeoutInMs: 2000, - }) - } - - async function iterate(prompt: string) { - tab.addChatMessage({ prompt }) - - await retryIfRequired( - async () => { - // Wait for a backend response - await tab.waitForChatFinishesLoading() - }, - () => {} - ) - } - - async function clickActionButton(filePath: string, actionName: string) { - tab.clickFileActionButton(filePath, actionName) - await tab.waitForEvent(() => !tab.hasAction(filePath, actionName), { - waitIntervalInMs: 500, - waitTimeoutInMs: 600000, - }) - } - - /** - * Wait for the original request to finish. - * If the response has a retry button or encountered a guardrails error, continue retrying - * - * This allows the e2e tests to recover from potential one off backend problems/random guardrails - */ - async function retryIfRequired(waitUntilReady: () => Promise, request?: () => void) { - await waitUntilReady() - - const findAnotherTopic = 'find another topic to discuss' - const tooManyRequests = 'Too many requests' - const failureState = (message: string) => { - return ( - tab.getChatItems().pop()?.body?.includes(message) || - tab.getChatItems().slice(-2).shift()?.body?.includes(message) - ) - } - while ( - tab.hasButton(FollowUpTypes.Retry) || - (request && (failureState(findAnotherTopic) || failureState(tooManyRequests))) - ) { - if (tab.hasButton(FollowUpTypes.Retry)) { - console.log('Retrying request') - tab.clickButton(FollowUpTypes.Retry) - await waitUntilReady() - } else if (failureState(tooManyRequests)) { - // 3 versions of the e2e tests are running at the same time in the ci so we occassionally need to wait before continuing - request && request() - await sleep(tooManyRequestsWaitTime) - } else { - // We've hit guardrails, re-make the request and wait again - request && request() - await waitUntilReady() - } - } - - // The backend never recovered - if (tab.hasButton(FollowUpTypes.SendFeedback)) { - assert.fail('Encountered an error when attempting to call the feature dev backend. Could not continue') - } - } - - before(async function () { - /** - * The tests are getting throttled, only run them on stable for now - * - * TODO: Re-enable for all versions once the backend can handle them - */ - const testVersion = process.env['VSCODE_TEST_VERSION'] - if (testVersion && testVersion !== 'stable') { - this.skip() - } - - await using(registerAuthHook('amazonq-test-account'), async () => { - await loginToIdC() - }) - }) - - beforeEach(() => { - registerAuthHook('amazonq-test-account') - framework = new qTestingFramework('featuredev', true, []) - tab = framework.createTab() - }) - - afterEach(() => { - framework.removeTab(tab.tabID) - framework.dispose() - sinon.restore() - }) - - describe('Quick action availability', () => { - it('Shows /dev when feature dev is enabled', async () => { - const command = tab.findCommand('/dev') - if (!command) { - assert.fail('Could not find command') - } - - if (command.length > 1) { - assert.fail('Found too many commands with the name /dev') - } - }) - - it('Does NOT show /dev when feature dev is NOT enabled', () => { - // The beforeEach registers a framework which accepts requests. If we don't dispose before building a new one we have duplicate messages - framework.dispose() - framework = new qTestingFramework('featuredev', false, []) - const tab = framework.createTab() - const command = tab.findCommand('/dev') - if (command.length > 0) { - assert.fail('Found command when it should not have been found') - } - }) - }) - - describe('/dev entry', () => { - before(async () => { - tab = framework.createTab() - tab.addChatMessage({ command: '/dev' }) // This would create a new tab for feature dev. - tab = framework.getSelectedTab() - }) - - it('should display information card', async () => { - await retryIfRequired( - async () => { - await tab.waitForChatFinishesLoading() - }, - () => { - const lastChatItems = tab.getChatItems().pop() - assert.deepStrictEqual(lastChatItems?.body, informationCard) - } - ) - }) - }) - - describe('/dev {msg} entry', async () => { - beforeEach(async function () { - const isMultiIterationTestsEnabled = process.env['AMAZONQ_FEATUREDEV_ITERATION_TEST'] // Controls whether to enable multiple iteration testing for Amazon Q feature development - if (!isMultiIterationTestsEnabled) { - this.skip() - } else { - this.timeout(900000) // Code Gen with multi-iterations requires longer than default timeout(5 mins). - } - tab = framework.createTab() - tab.addChatMessage({ command: '/dev', prompt }) - tab = framework.getSelectedTab() - await retryIfRequired( - async () => { - await tab.waitForChatFinishesLoading() - }, - () => {} - ) - }) - - afterEach(async function () { - // currentTest.state is undefined if a beforeEach fails - if ( - this.currentTest?.state === undefined || - this.currentTest?.isFailed() || - this.currentTest?.isPending() - ) { - // Since the tests are long running this may help in diagnosing the issue - console.log('Current chat items at failure') - console.log(JSON.stringify(tab.getChatItems(), undefined, 4)) - } - }) - - it('Clicks accept code and click new task', async () => { - await retryIfRequired(async () => { - await Promise.any([ - tab.waitForButtons([FollowUpTypes.InsertCode, FollowUpTypes.ProvideFeedbackAndRegenerateCode]), - tab.waitForButtons([FollowUpTypes.Retry]), - ]) - }) - tab.clickButton(FollowUpTypes.InsertCode) - await tab.waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession]) - tab.clickButton(FollowUpTypes.NewTask) - await waitForText('What new task would you like to work on?') - assert.deepStrictEqual(tab.getChatItems().pop()?.body, 'What new task would you like to work on?') - }) - - it('Iterates on codegen', async () => { - await retryIfRequired(async () => { - await Promise.any([ - tab.waitForButtons([FollowUpTypes.InsertCode, FollowUpTypes.ProvideFeedbackAndRegenerateCode]), - tab.waitForButtons([FollowUpTypes.Retry]), - ]) - }) - tab.clickButton(FollowUpTypes.ProvideFeedbackAndRegenerateCode) - await tab.waitForChatFinishesLoading() - await iterate(iteratePrompt) - tab.clickButton(FollowUpTypes.InsertCode) - await tab.waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession]) - }) - }) - - describe('file-level accepts', async () => { - beforeEach(async function () { - tab = framework.createTab() - tab.addChatMessage({ command: '/dev', prompt: fileLevelAcceptPrompt }) - tab = framework.getSelectedTab() - await retryIfRequired( - async () => { - await tab.waitForChatFinishesLoading() - }, - () => { - tab.addChatMessage({ prompt }) - } - ) - await retryIfRequired(async () => { - await Promise.any([ - tab.waitForButtons([FollowUpTypes.InsertCode, FollowUpTypes.ProvideFeedbackAndRegenerateCode]), - tab.waitForButtons([FollowUpTypes.Retry]), - ]) - }) - }) - - describe('fileList', async () => { - it('has both accept-change and reject-change action buttons for file', async () => { - const filePath = tab.getFilePaths()[0] - assert.ok(tab.getActionsByFilePath(filePath).length === 2) - assert.ok(tab.hasAction(filePath, 'accept-change')) - assert.ok(tab.hasAction(filePath, 'reject-change')) - }) - - it('has only revert-rejection action button for rejected file', async () => { - const filePath = tab.getFilePaths()[0] - await clickActionButton(filePath, 'reject-change') - - assert.ok(tab.getActionsByFilePath(filePath).length === 1) - assert.ok(tab.hasAction(filePath, 'revert-rejection')) - }) - - it('does not have any of the action buttons for accepted file', async () => { - const filePath = tab.getFilePaths()[0] - await clickActionButton(filePath, 'accept-change') - - assert.ok(tab.getActionsByFilePath(filePath).length === 0) - }) - - it('disables all action buttons when new task is clicked', async () => { - tab.clickButton(FollowUpTypes.InsertCode) - await tab.waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession]) - tab.clickButton(FollowUpTypes.NewTask) - await waitForText('What new task would you like to work on?') - - const filePaths = tab.getFilePaths() - for (const filePath of filePaths) { - assert.ok(tab.getActionsByFilePath(filePath).length === 0) - } - }) - - it('disables all action buttons when close session is clicked', async () => { - tab.clickButton(FollowUpTypes.InsertCode) - await tab.waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession]) - tab.clickButton(FollowUpTypes.CloseSession) - await waitForText( - "Okay, I've ended this chat session. You can open a new tab to chat or start another workflow." - ) - - const filePaths = tab.getFilePaths() - for (const filePath of filePaths) { - assert.ok(tab.getActionsByFilePath(filePath).length === 0) - } - }) - }) - - describe('accept button', async () => { - describe('button text', async () => { - it('shows "Accept all changes" when no files are accepted or rejected, and "Accept remaining changes" otherwise', async () => { - let insertCodeButton = tab.getFollowUpButton(FollowUpTypes.InsertCode) - assert.ok(insertCodeButton.pillText === 'Accept all changes') - - const filePath = tab.getFilePaths()[0] - await clickActionButton(filePath, 'reject-change') - - insertCodeButton = tab.getFollowUpButton(FollowUpTypes.InsertCode) - assert.ok(insertCodeButton.pillText === 'Accept remaining changes') - - await clickActionButton(filePath, 'revert-rejection') - - insertCodeButton = tab.getFollowUpButton(FollowUpTypes.InsertCode) - assert.ok(insertCodeButton.pillText === 'Accept all changes') - - await clickActionButton(filePath, 'accept-change') - - insertCodeButton = tab.getFollowUpButton(FollowUpTypes.InsertCode) - assert.ok(insertCodeButton.pillText === 'Accept remaining changes') - }) - - it('shows "Continue" when all files are either accepted or rejected, with at least one of them rejected', async () => { - const filePaths = tab.getFilePaths() - for (const filePath of filePaths) { - await clickActionButton(filePath, 'reject-change') - } - - const insertCodeButton = tab.getFollowUpButton(FollowUpTypes.InsertCode) - assert.ok(insertCodeButton.pillText === 'Continue') - }) - }) - - it('disappears and automatically moves on to the next step when all changes are accepted', async () => { - const filePaths = tab.getFilePaths() - for (const filePath of filePaths) { - await clickActionButton(filePath, 'accept-change') - } - await tab.waitForButtons([FollowUpTypes.NewTask, FollowUpTypes.CloseSession]) - - assert.ok(tab.hasButton(FollowUpTypes.InsertCode) === false) - assert.ok(tab.hasButton(FollowUpTypes.ProvideFeedbackAndRegenerateCode) === false) - }) - }) - }) -}) diff --git a/packages/amazonq/test/e2e/amazonq/testGen.test.ts b/packages/amazonq/test/e2e/amazonq/testGen.test.ts deleted file mode 100644 index 21db83fd6e8..00000000000 --- a/packages/amazonq/test/e2e/amazonq/testGen.test.ts +++ /dev/null @@ -1,209 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import assert from 'assert' -import vscode from 'vscode' -import { qTestingFramework } from './framework/framework' -import sinon from 'sinon' -import { Messenger } from './framework/messenger' -import { FollowUpTypes } from 'aws-core-vscode/amazonq' -import { registerAuthHook, using, TestFolder, closeAllEditors, getTestWorkspaceFolder } from 'aws-core-vscode/test' -import { loginToIdC } from './utils/setup' -import { waitUntil, workspaceUtils } from 'aws-core-vscode/shared' -import * as path from 'path' - -describe('Amazon Q Test Generation', function () { - let framework: qTestingFramework - let tab: Messenger - - const testFiles = [ - { - language: 'python', - filePath: 'testGenFolder/src/main/math.py', - testFilePath: 'testGenFolder/src/test/test_math.py', - }, - { - language: 'java', - filePath: 'testGenFolder/src/main/Math.java', - testFilePath: 'testGenFolder/src/test/MathTest.java', - }, - ] - - // handles opening the file since /test must be called on an active file - async function setupTestDocument(filePath: string, language: string) { - const document = await waitUntil(async () => { - const doc = await workspaceUtils.openTextDocument(filePath) - return doc - }, {}) - - if (!document) { - assert.fail(`Failed to open ${language} file`) - } - - await waitUntil(async () => { - await vscode.window.showTextDocument(document, { preview: false }) - }, {}) - - const activeEditor = vscode.window.activeTextEditor - if (!activeEditor || activeEditor.document.uri.fsPath !== document.uri.fsPath) { - assert.fail(`Failed to make ${language} file active`) - } - } - - async function waitForChatItems(index: number) { - await tab.waitForEvent(() => tab.getChatItems().length > index, { - waitTimeoutInMs: 5000, - waitIntervalInMs: 1000, - }) - } - - // clears test file to a blank file - // not cleaning up test file may possibly cause bloat in CI since testFixtures does not get reset - async function cleanupTestFile(testFilePath: string) { - const workspaceFolder = getTestWorkspaceFolder() - const absoluteTestFilePath = path.join(workspaceFolder, testFilePath) - const testFileUri = vscode.Uri.file(absoluteTestFilePath) - await vscode.workspace.fs.writeFile(testFileUri, Buffer.from('', 'utf-8')) - } - - before(async function () { - await using(registerAuthHook('amazonq-test-account'), async () => { - await loginToIdC() - }) - }) - - beforeEach(async () => { - registerAuthHook('amazonq-test-account') - framework = new qTestingFramework('testgen', true, []) - tab = framework.createTab() - }) - - afterEach(async () => { - // Close all editors to prevent conflicts with subsequent tests trying to open the same file - await closeAllEditors() - framework.removeTab(tab.tabID) - framework.dispose() - sinon.restore() - }) - - describe('Quick action availability', () => { - it('Shows /test when test generation is enabled', async () => { - const command = tab.findCommand('/test') - if (!command.length) { - assert.fail('Could not find command') - } - if (command.length > 1) { - assert.fail('Found too many commands with the name /test') - } - }) - - it('Does NOT show /test when test generation is NOT enabled', () => { - // The beforeEach registers a framework which accepts requests. If we don't dispose before building a new one we have duplicate messages - framework.dispose() - framework = new qTestingFramework('testgen', false, []) - const tab = framework.createTab() - const command = tab.findCommand('/test') - if (command.length > 0) { - assert.fail('Found command when it should not have been found') - } - }) - }) - - describe('/test entry', () => { - describe('External file out of project', async () => { - let testFolder: TestFolder - let fileName: string - - beforeEach(async () => { - testFolder = await TestFolder.create() - fileName = 'math.py' - const filePath = await testFolder.write(fileName, 'def add(a, b): return a + b') - - const document = await vscode.workspace.openTextDocument(filePath) - await vscode.window.showTextDocument(document, { preview: false }) - }) - - it('/test for external file redirects to chat', async () => { - tab.addChatMessage({ command: '/test' }) - await tab.waitForChatFinishesLoading() - - await waitForChatItems(3) - const externalFileMessage = tab.getChatItems()[3] - - assert.deepStrictEqual(externalFileMessage.type, 'answer') - assert.deepStrictEqual( - externalFileMessage.body, - `I can't generate tests for ${fileName} because the file is outside of workspace scope.
I can still provide examples, instructions and code suggestions.` - ) - }) - }) - - for (const { language, filePath, testFilePath } of testFiles) { - describe(`/test on ${language} file`, () => { - beforeEach(async () => { - await waitUntil(async () => await setupTestDocument(filePath, language), {}) - - tab.addChatMessage({ command: '/test' }) - await tab.waitForChatFinishesLoading() - - await tab.waitForButtons([FollowUpTypes.ViewDiff]) - tab.clickButton(FollowUpTypes.ViewDiff) - await tab.waitForChatFinishesLoading() - }) - - describe('View diff of test file', async () => { - it('Clicks on view diff', async () => { - const chatItems = tab.getChatItems() - const viewDiffMessage = chatItems[5] - - assert.deepStrictEqual(viewDiffMessage.type, 'answer') - const expectedEnding = - 'Please see the unit tests generated below. Click “View diff” to review the changes in the code editor.' - assert.strictEqual( - viewDiffMessage.body?.includes(expectedEnding), - true, - `View diff message does not contain phrase: ${expectedEnding}` - ) - }) - }) - - describe('Accept unit tests', async () => { - afterEach(async () => { - // this e2e test generates unit tests, so we want to clean them up after this test is done - await waitUntil(async () => { - await cleanupTestFile(testFilePath) - }, {}) - }) - - it('Clicks on accept', async () => { - await tab.waitForButtons([FollowUpTypes.AcceptCode, FollowUpTypes.RejectCode]) - tab.clickButton(FollowUpTypes.AcceptCode) - await tab.waitForChatFinishesLoading() - - await waitForChatItems(7) - const acceptedMessage = tab.getChatItems()[7] - - assert.deepStrictEqual(acceptedMessage?.type, 'answer-part') - assert.deepStrictEqual(acceptedMessage?.followUp?.options?.[0].pillText, 'Accepted') - }) - }) - - describe('Reject unit tests', async () => { - it('Clicks on reject', async () => { - await tab.waitForButtons([FollowUpTypes.AcceptCode, FollowUpTypes.RejectCode]) - tab.clickButton(FollowUpTypes.RejectCode) - await tab.waitForChatFinishesLoading() - - await waitForChatItems(7) - const rejectedMessage = tab.getChatItems()[7] - - assert.deepStrictEqual(rejectedMessage?.type, 'answer-part') - assert.deepStrictEqual(rejectedMessage?.followUp?.options?.[0].pillText, 'Rejected') - }) - }) - }) - } - }) -}) diff --git a/packages/core/src/amazonq/indexNode.ts b/packages/core/src/amazonq/indexNode.ts index 88a3a4bba37..628b5d626cd 100644 --- a/packages/core/src/amazonq/indexNode.ts +++ b/packages/core/src/amazonq/indexNode.ts @@ -7,7 +7,6 @@ * These agents have underlying requirements on node dependencies (e.g. jsdom, admzip) */ export { init as cwChatAppInit } from '../codewhispererChat/app' -export { init as featureDevChatAppInit } from '../amazonqFeatureDev/app' +export { init as featureDevChatAppInit } from '../amazonqFeatureDev/app' // TODO: Remove this export { init as gumbyChatAppInit } from '../amazonqGumby/app' -export { init as testChatAppInit } from '../amazonqTest/app' -export { init as docChatAppInit } from '../amazonqDoc/app' +export { init as docChatAppInit } from '../amazonqDoc/app' // TODO: Remove this diff --git a/packages/core/src/amazonq/webview/ui/apps/amazonqCommonsConnector.ts b/packages/core/src/amazonq/webview/ui/apps/amazonqCommonsConnector.ts index 04ed6907795..68983b6c188 100644 --- a/packages/core/src/amazonq/webview/ui/apps/amazonqCommonsConnector.ts +++ b/packages/core/src/amazonq/webview/ui/apps/amazonqCommonsConnector.ts @@ -7,13 +7,7 @@ import { ChatItem, ChatItemAction, ChatItemType, ChatPrompt } from '@aws/mynah-u import { ExtensionMessage } from '../commands' import { AuthFollowUpType } from '../followUps/generator' import { getTabCommandFromTabType, isTabType, TabType } from '../storages/tabsStorage' -import { - docUserGuide, - userGuideURL as featureDevUserGuide, - helpMessage, - reviewGuideUrl, - testGuideUrl, -} from '../texts/constants' +import { helpMessage, reviewGuideUrl } from '../texts/constants' import { linkToDocsHome } from '../../../../codewhisperer/models/constants' import { createClickTelemetry, createOpenAgentTelemetry } from '../telemetry/actions' @@ -110,18 +104,9 @@ export class Connector { private processUserGuideLink(tabType: TabType, actionId: string) { let userGuideLink = '' switch (tabType) { - case 'featuredev': - userGuideLink = featureDevUserGuide - break - case 'testgen': - userGuideLink = testGuideUrl - break case 'review': userGuideLink = reviewGuideUrl break - case 'doc': - userGuideLink = docUserGuide - break case 'gumby': userGuideLink = linkToDocsHome break diff --git a/packages/core/src/amazonq/webview/ui/apps/docChatConnector.ts b/packages/core/src/amazonq/webview/ui/apps/docChatConnector.ts deleted file mode 100644 index 96822c8336c..00000000000 --- a/packages/core/src/amazonq/webview/ui/apps/docChatConnector.ts +++ /dev/null @@ -1,226 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { ChatItem, ChatItemType, FeedbackPayload, MynahIcons, ProgressField } from '@aws/mynah-ui' -import { TabType } from '../storages/tabsStorage' -import { DiffTreeFileInfo } from '../diffTree/types' -import { BaseConnectorProps, BaseConnector } from './baseConnector' - -export interface ConnectorProps extends BaseConnectorProps { - onAsyncEventProgress: (tabID: string, inProgress: boolean, message: string) => void - sendFeedback?: (tabId: string, feedbackPayload: FeedbackPayload) => void | undefined - onFileComponentUpdate: ( - tabID: string, - filePaths: DiffTreeFileInfo[], - deletedFiles: DiffTreeFileInfo[], - messageId: string, - disableFileActions: boolean - ) => void - onFileActionClick: (tabID: string, messageId: string, filePath: string, actionName: string) => void - onUpdatePlaceholder: (tabID: string, newPlaceholder: string) => void - onUpdatePromptProgress: (tabID: string, progressField: ProgressField) => void - onChatInputEnabled: (tabID: string, enabled: boolean) => void - onUpdateAuthentication: (featureDevEnabled: boolean, authenticatingTabIDs: string[]) => void -} - -export class Connector extends BaseConnector { - private readonly onFileComponentUpdate - private readonly onAsyncEventProgress - private readonly updatePlaceholder - private readonly chatInputEnabled - private readonly onUpdateAuthentication - private readonly updatePromptProgress - - override getTabType(): TabType { - return 'doc' - } - - constructor(props: ConnectorProps) { - super(props) - this.onFileComponentUpdate = props.onFileComponentUpdate - this.onAsyncEventProgress = props.onAsyncEventProgress - this.updatePlaceholder = props.onUpdatePlaceholder - this.chatInputEnabled = props.onChatInputEnabled - this.onUpdateAuthentication = props.onUpdateAuthentication - this.updatePromptProgress = props.onUpdatePromptProgress - } - - onOpenDiff = (tabID: string, filePath: string, deleted: boolean): void => { - this.sendMessageToExtension({ - command: 'open-diff', - tabID, - filePath, - deleted, - tabType: this.getTabType(), - }) - } - onFileActionClick = (tabID: string, messageId: string, filePath: string, actionName: string): void => { - this.sendMessageToExtension({ - command: 'file-click', - tabID, - messageId, - filePath, - actionName, - tabType: this.getTabType(), - }) - } - - private processFolderConfirmationMessage = async (messageData: any, folderPath: string): Promise => { - if (this.onChatAnswerReceived !== undefined) { - const answer: ChatItem = { - type: ChatItemType.ANSWER, - body: messageData.message ?? undefined, - messageId: messageData.messageID ?? messageData.triggerID ?? '', - fileList: { - rootFolderTitle: undefined, - fileTreeTitle: '', - filePaths: [folderPath], - details: { - [folderPath]: { - icon: MynahIcons.FOLDER, - clickable: false, - }, - }, - }, - followUp: { - text: '', - options: messageData.followUps, - }, - } - this.onChatAnswerReceived(messageData.tabID, answer, messageData) - } - } - - private processChatMessage = async (messageData: any): Promise => { - if (this.onChatAnswerReceived !== undefined) { - const answer: ChatItem = { - type: messageData.messageType, - body: messageData.message ?? undefined, - messageId: messageData.messageID ?? messageData.triggerID ?? '', - relatedContent: undefined, - canBeVoted: messageData.canBeVoted, - snapToTop: messageData.snapToTop, - followUp: - messageData.followUps !== undefined && messageData.followUps.length > 0 - ? { - text: - messageData.messageType === ChatItemType.SYSTEM_PROMPT - ? '' - : 'Select one of the following...', - options: messageData.followUps, - } - : undefined, - } - this.onChatAnswerReceived(messageData.tabID, answer, messageData) - } - } - - private processCodeResultMessage = async (messageData: any): Promise => { - if (this.onChatAnswerReceived !== undefined) { - const answer: ChatItem = { - type: ChatItemType.ANSWER, - relatedContent: undefined, - followUp: undefined, - canBeVoted: false, - codeReference: messageData.references, - // TODO get the backend to store a message id in addition to conversationID - messageId: - messageData.codeGenerationId ?? - messageData.messageID ?? - messageData.triggerID ?? - messageData.conversationID, - fileList: { - rootFolderTitle: 'Documentation', - fileTreeTitle: 'Documents ready', - filePaths: messageData.filePaths.map((f: DiffTreeFileInfo) => f.zipFilePath), - deletedFiles: messageData.deletedFiles.map((f: DiffTreeFileInfo) => f.zipFilePath), - }, - body: '', - } - this.onChatAnswerReceived(messageData.tabID, answer, messageData) - } - } - - handleMessageReceive = async (messageData: any): Promise => { - if (messageData.type === 'updateFileComponent') { - this.onFileComponentUpdate( - messageData.tabID, - messageData.filePaths, - messageData.deletedFiles, - messageData.messageId, - messageData.disableFileActions - ) - return - } - - if (messageData.type === 'chatMessage') { - await this.processChatMessage(messageData) - return - } - - if (messageData.type === 'folderConfirmationMessage') { - await this.processFolderConfirmationMessage(messageData, messageData.folderPath) - return - } - - if (messageData.type === 'codeResultMessage') { - await this.processCodeResultMessage(messageData) - return - } - - if (messageData.type === 'asyncEventProgressMessage') { - this.onAsyncEventProgress(messageData.tabID, messageData.inProgress, messageData.message ?? undefined) - return - } - - if (messageData.type === 'updatePlaceholderMessage') { - this.updatePlaceholder(messageData.tabID, messageData.newPlaceholder) - return - } - - if (messageData.type === 'chatInputEnabledMessage') { - this.chatInputEnabled(messageData.tabID, messageData.enabled) - return - } - - if (messageData.type === 'authenticationUpdateMessage') { - this.onUpdateAuthentication(messageData.featureEnabled, messageData.authenticatingTabIDs) - return - } - - if (messageData.type === 'openNewTabMessage') { - this.onNewTab(this.getTabType()) - return - } - - if (messageData.type === 'updatePromptProgress') { - this.updatePromptProgress(messageData.tabID, messageData.progressField) - return - } - - // For other message types, call the base class handleMessageReceive - await this.baseHandleMessageReceive(messageData) - } - - onCustomFormAction( - tabId: string, - action: { - id: string - text?: string | undefined - formItemValues?: Record | undefined - } - ) { - if (action === undefined) { - return - } - this.sendMessageToExtension({ - command: 'form-action-click', - action: action.id, - formSelectedValues: action.formItemValues, - tabType: 'doc', - tabID: tabId, - }) - } -} diff --git a/packages/core/src/amazonq/webview/ui/apps/featureDevChatConnector.ts b/packages/core/src/amazonq/webview/ui/apps/featureDevChatConnector.ts deleted file mode 100644 index 1f6d33a1ec4..00000000000 --- a/packages/core/src/amazonq/webview/ui/apps/featureDevChatConnector.ts +++ /dev/null @@ -1,212 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { ChatItem, ChatItemType, FeedbackPayload } from '@aws/mynah-ui' -import { TabType } from '../storages/tabsStorage' -import { getActions } from '../diffTree/actions' -import { DiffTreeFileInfo } from '../diffTree/types' -import { BaseConnector, BaseConnectorProps } from './baseConnector' - -export interface ConnectorProps extends BaseConnectorProps { - onAsyncEventProgress: ( - tabID: string, - inProgress: boolean, - message: string, - messageId: string | undefined, - enableStopAction: boolean - ) => void - onChatAnswerUpdated?: (tabID: string, message: ChatItem) => void - sendFeedback?: (tabId: string, feedbackPayload: FeedbackPayload) => void | undefined - onFileComponentUpdate: ( - tabID: string, - filePaths: DiffTreeFileInfo[], - deletedFiles: DiffTreeFileInfo[], - messageId: string, - disableFileActions: boolean - ) => void - onFileActionClick: (tabID: string, messageId: string, filePath: string, actionName: string) => void - onUpdatePlaceholder: (tabID: string, newPlaceholder: string) => void - onChatInputEnabled: (tabID: string, enabled: boolean) => void - onUpdateAuthentication: (featureDevEnabled: boolean, authenticatingTabIDs: string[]) => void -} - -export class Connector extends BaseConnector { - private readonly onFileComponentUpdate - private readonly onChatAnswerUpdated - private readonly onAsyncEventProgress - private readonly updatePlaceholder - private readonly chatInputEnabled - private readonly onUpdateAuthentication - - override getTabType(): TabType { - return 'featuredev' - } - - constructor(props: ConnectorProps) { - super(props) - this.onFileComponentUpdate = props.onFileComponentUpdate - this.onAsyncEventProgress = props.onAsyncEventProgress - this.updatePlaceholder = props.onUpdatePlaceholder - this.chatInputEnabled = props.onChatInputEnabled - this.onUpdateAuthentication = props.onUpdateAuthentication - this.onChatAnswerUpdated = props.onChatAnswerUpdated - } - - onOpenDiff = (tabID: string, filePath: string, deleted: boolean, messageId?: string): void => { - this.sendMessageToExtension({ - command: 'open-diff', - tabID, - filePath, - deleted, - messageId, - tabType: this.getTabType(), - }) - } - onFileActionClick = (tabID: string, messageId: string, filePath: string, actionName: string): void => { - this.sendMessageToExtension({ - command: 'file-click', - tabID, - messageId, - filePath, - actionName, - tabType: this.getTabType(), - }) - } - - private createAnswer = (messageData: any): ChatItem => { - return { - type: messageData.messageType, - body: messageData.message ?? undefined, - messageId: messageData.messageId ?? messageData.messageID ?? messageData.triggerID ?? '', - relatedContent: undefined, - canBeVoted: messageData.canBeVoted ?? undefined, - snapToTop: messageData.snapToTop ?? undefined, - followUp: - messageData.followUps !== undefined && Array.isArray(messageData.followUps) - ? { - text: - messageData.messageType === ChatItemType.SYSTEM_PROMPT || - messageData.followUps.length === 0 - ? '' - : 'Please follow up with one of these', - options: messageData.followUps, - } - : undefined, - } - } - - private processChatMessage = async (messageData: any): Promise => { - if (this.onChatAnswerReceived !== undefined) { - const answer = this.createAnswer(messageData) - this.onChatAnswerReceived(messageData.tabID, answer, messageData) - } - } - - private processCodeResultMessage = async (messageData: any): Promise => { - if (this.onChatAnswerReceived !== undefined) { - const messageId = - messageData.codeGenerationId ?? - messageData.messageId ?? - messageData.messageID ?? - messageData.triggerID ?? - messageData.conversationID - this.sendMessageToExtension({ - tabID: messageData.tabID, - command: 'store-code-result-message-id', - messageId, - tabType: 'featuredev', - }) - const actions = getActions([...messageData.filePaths, ...messageData.deletedFiles]) - const answer: ChatItem = { - type: ChatItemType.ANSWER, - relatedContent: undefined, - followUp: undefined, - canBeVoted: true, - codeReference: messageData.references, - messageId, - fileList: { - rootFolderTitle: 'Changes', - filePaths: messageData.filePaths.map((f: DiffTreeFileInfo) => f.zipFilePath), - deletedFiles: messageData.deletedFiles.map((f: DiffTreeFileInfo) => f.zipFilePath), - actions, - }, - body: '', - } - this.onChatAnswerReceived(messageData.tabID, answer, messageData) - } - } - - handleMessageReceive = async (messageData: any): Promise => { - if (messageData.type === 'updateFileComponent') { - this.onFileComponentUpdate( - messageData.tabID, - messageData.filePaths, - messageData.deletedFiles, - messageData.messageId, - messageData.disableFileActions - ) - return - } - if (messageData.type === 'updateChatAnswer') { - const answer = this.createAnswer(messageData) - this.onChatAnswerUpdated?.(messageData.tabID, answer) - return - } - - if (messageData.type === 'chatMessage') { - await this.processChatMessage(messageData) - return - } - - if (messageData.type === 'codeResultMessage') { - await this.processCodeResultMessage(messageData) - return - } - - if (messageData.type === 'asyncEventProgressMessage') { - const enableStopAction = true - this.onAsyncEventProgress( - messageData.tabID, - messageData.inProgress, - messageData.message ?? undefined, - messageData.messageId ?? undefined, - enableStopAction - ) - return - } - - if (messageData.type === 'updatePlaceholderMessage') { - this.updatePlaceholder(messageData.tabID, messageData.newPlaceholder) - return - } - - if (messageData.type === 'chatInputEnabledMessage') { - this.chatInputEnabled(messageData.tabID, messageData.enabled) - return - } - - if (messageData.type === 'authenticationUpdateMessage') { - this.onUpdateAuthentication(messageData.featureEnabled, messageData.authenticatingTabIDs) - return - } - - if (messageData.type === 'openNewTabMessage') { - this.onNewTab('featuredev') - return - } - - // For other message types, call the base class handleMessageReceive - await this.baseHandleMessageReceive(messageData) - } - - sendFeedback = (tabId: string, feedbackPayload: FeedbackPayload): void | undefined => { - this.sendMessageToExtension({ - command: 'chat-item-feedback', - ...feedbackPayload, - tabType: this.getTabType(), - tabID: tabId, - }) - } -} diff --git a/packages/core/src/amazonq/webview/ui/apps/testChatConnector.ts b/packages/core/src/amazonq/webview/ui/apps/testChatConnector.ts deleted file mode 100644 index 35fb0bc0683..00000000000 --- a/packages/core/src/amazonq/webview/ui/apps/testChatConnector.ts +++ /dev/null @@ -1,293 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - * - * This class is responsible for listening to and processing events - * from the webview and translating them into events to be handled by the extension, - * and events from the extension and translating them into events to be handled by the webview. - */ - -import { ChatItem, ChatItemType, MynahIcons, ProgressField } from '@aws/mynah-ui' -import { ExtensionMessage } from '../commands' -import { TabsStorage, TabType } from '../storages/tabsStorage' -import { TestMessageType } from '../../../../amazonqTest/chat/views/connector/connector' -import { ChatPayload } from '../connector' -import { BaseConnector, BaseConnectorProps } from './baseConnector' -import { FollowUpTypes } from '../../../commons/types' - -export interface ConnectorProps extends BaseConnectorProps { - sendMessageToExtension: (message: ExtensionMessage) => void - onChatAnswerReceived?: (tabID: string, message: ChatItem, messageData: any) => void - onRunTestMessageReceived?: (tabID: string, showRunTestMessage: boolean) => void - onChatAnswerUpdated?: (tabID: string, message: ChatItem) => void - onQuickHandlerCommand: (tabID: string, command: string, eventId?: string) => void - onWarning: (tabID: string, message: string, title: string) => void - onError: (tabID: string, message: string, title: string) => void - onUpdateAuthentication: (testEnabled: boolean, authenticatingTabIDs: string[]) => void - onChatInputEnabled: (tabID: string, enabled: boolean) => void - onUpdatePlaceholder: (tabID: string, newPlaceholder: string) => void - onUpdatePromptProgress: (tabID: string, progressField: ProgressField) => void - tabsStorage: TabsStorage -} - -export interface MessageData { - tabID: string - type: TestMessageType -} -// TODO: Refactor testChatConnector, scanChatConnector and other apps connector files post RIV -export class Connector extends BaseConnector { - override getTabType(): TabType { - return 'testgen' - } - readonly onAuthenticationUpdate - override readonly sendMessageToExtension - override readonly onChatAnswerReceived - private readonly onChatAnswerUpdated - private readonly chatInputEnabled - private readonly updatePlaceholder - private readonly updatePromptProgress - override readonly onError - private readonly tabStorage - private readonly runTestMessageReceived - - constructor(props: ConnectorProps) { - super(props) - this.runTestMessageReceived = props.onRunTestMessageReceived - this.sendMessageToExtension = props.sendMessageToExtension - this.onChatAnswerReceived = props.onChatAnswerReceived - this.onChatAnswerUpdated = props.onChatAnswerUpdated - this.chatInputEnabled = props.onChatInputEnabled - this.updatePlaceholder = props.onUpdatePlaceholder - this.updatePromptProgress = props.onUpdatePromptProgress - this.onAuthenticationUpdate = props.onUpdateAuthentication - this.onError = props.onError - this.tabStorage = props.tabsStorage - } - - startTestGen(tabID: string, prompt: string) { - this.sendMessageToExtension({ - tabID: tabID, - command: 'start-test-gen', - tabType: 'testgen', - prompt, - }) - } - - requestAnswer = (tabID: string, payload: ChatPayload) => { - this.tabStorage.updateTabStatus(tabID, 'busy') - this.sendMessageToExtension({ - tabID: tabID, - command: 'chat-prompt', - chatMessage: payload.chatMessage, - chatCommand: payload.chatCommand, - tabType: 'testgen', - }) - } - - onCustomFormAction( - tabId: string, - messageId: string, - action: { - id: string - text?: string | undefined - description?: string | undefined - formItemValues?: Record | undefined - } - ) { - if (action === undefined) { - return - } - - this.sendMessageToExtension({ - command: 'form-action-click', - action: action.id, - formSelectedValues: action.formItemValues, - tabType: 'testgen', - tabID: tabId, - description: action.description, - }) - - if (this.onChatAnswerUpdated === undefined) { - return - } - const answer: ChatItem = { - type: ChatItemType.ANSWER, - messageId: messageId, - buttons: [], - } - // TODO: Add more cases for Accept/Reject/viewDiff. - switch (action.id) { - case 'Provide-Feedback': - answer.buttons = [ - { - keepCardAfterClick: true, - text: 'Thanks for providing feedback.', - id: 'utg_provided_feedback', - status: 'success', - position: 'outside', - disabled: true, - }, - ] - break - default: - break - } - this.onChatAnswerUpdated(tabId, answer) - } - - onFileDiff = (tabID: string, filePath: string, deleted: boolean, messageId?: string): void => { - if (this.onChatAnswerReceived === undefined) { - return - } - // Open diff view - this.sendMessageToExtension({ - command: 'open-diff', - tabID, - filePath, - deleted, - messageId, - tabType: 'testgen', - }) - this.onChatAnswerReceived( - tabID, - { - type: ChatItemType.ANSWER, - messageId: messageId, - followUp: { - text: ' ', - options: [ - { - type: FollowUpTypes.AcceptCode, - pillText: 'Accept', - status: 'success', - icon: MynahIcons.OK, - }, - { - type: FollowUpTypes.RejectCode, - pillText: 'Reject', - status: 'error', - icon: MynahIcons.REVERT, - }, - ], - }, - }, - {} - ) - } - - private processChatMessage = async (messageData: any): Promise => { - if (this.onChatAnswerReceived === undefined) { - return - } - if (messageData.command === 'test' && this.runTestMessageReceived) { - this.runTestMessageReceived(messageData.tabID, true) - return - } - if (messageData.message !== undefined) { - const answer: ChatItem = { - type: messageData.messageType, - messageId: messageData.messageId ?? messageData.triggerID, - body: messageData.message, - canBeVoted: false, - informationCard: messageData.informationCard, - buttons: messageData.buttons ?? [], - } - this.onChatAnswerReceived(messageData.tabID, answer, messageData) - } - } - // Displays the test generation summary message in the /test Tab before generating unit tests - private processChatSummaryMessage = async (messageData: any): Promise => { - if (this.onChatAnswerUpdated === undefined) { - return - } - if (messageData.message !== undefined) { - const answer: ChatItem = { - type: messageData.messageType, - messageId: messageData.messageId ?? messageData.triggerID, - body: messageData.message, - canBeVoted: true, - footer: messageData.filePath - ? { - fileList: { - rootFolderTitle: undefined, - fileTreeTitle: '', - filePaths: [messageData.filePath], - details: { - [messageData.filePath]: { - icon: MynahIcons.FILE, - description: `Generating tests in ${messageData.filePath}`, - }, - }, - }, - } - : {}, - } - this.onChatAnswerUpdated(messageData.tabID, answer) - } - } - - override processAuthNeededException = async (messageData: any): Promise => { - if (this.onChatAnswerReceived === undefined) { - return - } - - this.onChatAnswerReceived( - messageData.tabID, - { - type: ChatItemType.SYSTEM_PROMPT, - body: messageData.message, - }, - messageData - ) - } - - private processBuildProgressMessage = async ( - messageData: { type: TestMessageType } & Record - ): Promise => { - if (this.onChatAnswerReceived === undefined) { - return - } - const answer: ChatItem = { - type: messageData.messageType, - canBeVoted: messageData.canBeVoted, - messageId: messageData.messageId, - followUp: messageData.followUps, - fileList: messageData.fileList, - body: messageData.message, - codeReference: messageData.codeReference, - } - this.onChatAnswerReceived(messageData.tabID, answer, messageData) - } - - // This handles messages received from the extension, to be forwarded to the webview - handleMessageReceive = async (messageData: { type: TestMessageType } & Record) => { - switch (messageData.type) { - case 'authNeededException': - await this.processAuthNeededException(messageData) - break - case 'authenticationUpdateMessage': - this.onAuthenticationUpdate(messageData.testEnabled, messageData.authenticatingTabIDs) - break - case 'chatInputEnabledMessage': - this.chatInputEnabled(messageData.tabID, messageData.enabled) - break - case 'chatMessage': - await this.processChatMessage(messageData) - break - case 'chatSummaryMessage': - await this.processChatSummaryMessage(messageData) - break - case 'updatePlaceholderMessage': - this.updatePlaceholder(messageData.tabID, messageData.newPlaceholder) - break - case 'buildProgressMessage': - await this.processBuildProgressMessage(messageData) - break - case 'updatePromptProgress': - this.updatePromptProgress(messageData.tabID, messageData.progressField) - break - case 'errorMessage': - this.onError(messageData.tabID, messageData.message, messageData.title) - } - } -} diff --git a/packages/core/src/amazonq/webview/ui/connector.ts b/packages/core/src/amazonq/webview/ui/connector.ts index 1c31f6cc842..cc1b010375a 100644 --- a/packages/core/src/amazonq/webview/ui/connector.ts +++ b/packages/core/src/amazonq/webview/ui/connector.ts @@ -19,12 +19,9 @@ import { DetailedList, } from '@aws/mynah-ui' import { Connector as CWChatConnector } from './apps/cwChatConnector' -import { Connector as FeatureDevChatConnector } from './apps/featureDevChatConnector' import { Connector as AmazonQCommonsConnector } from './apps/amazonqCommonsConnector' import { Connector as GumbyChatConnector } from './apps/gumbyChatConnector' import { Connector as ScanChatConnector } from './apps/scanChatConnector' -import { Connector as TestChatConnector } from './apps/testChatConnector' -import { Connector as docChatConnector } from './apps/docChatConnector' import { ExtensionMessage } from './commands' import { TabType, TabsStorage } from './storages/tabsStorage' import { WelcomeFollowupType } from './apps/amazonqCommonsConnector' @@ -123,11 +120,8 @@ export class Connector { private readonly sendMessageToExtension private readonly onMessageReceived private readonly cwChatConnector - private readonly featureDevChatConnector private readonly gumbyChatConnector private readonly scanChatConnector - private readonly testChatConnector - private readonly docChatConnector private readonly tabsStorage private readonly amazonqCommonsConnector: AmazonQCommonsConnector @@ -137,11 +131,8 @@ export class Connector { this.sendMessageToExtension = props.sendMessageToExtension this.onMessageReceived = props.onMessageReceived this.cwChatConnector = new CWChatConnector(props as ConnectorProps) - this.featureDevChatConnector = new FeatureDevChatConnector(props) - this.docChatConnector = new docChatConnector(props) this.gumbyChatConnector = new GumbyChatConnector(props) this.scanChatConnector = new ScanChatConnector(props) - this.testChatConnector = new TestChatConnector(props) this.amazonqCommonsConnector = new AmazonQCommonsConnector({ sendMessageToExtension: this.sendMessageToExtension, onWelcomeFollowUpClicked: props.onWelcomeFollowUpClicked, @@ -172,20 +163,12 @@ export class Connector { case 'cwc': this.cwChatConnector.onResponseBodyLinkClick(tabID, messageId, link) break - case 'featuredev': - this.featureDevChatConnector.onResponseBodyLinkClick(tabID, messageId, link) - break case 'gumby': this.gumbyChatConnector.onResponseBodyLinkClick(tabID, messageId, link) break case 'review': this.scanChatConnector.onResponseBodyLinkClick(tabID, messageId, link) break - case 'testgen': - this.testChatConnector.onResponseBodyLinkClick(tabID, messageId, link) - break - case 'doc': - this.docChatConnector.onResponseBodyLinkClick(tabID, messageId, link) } } @@ -201,8 +184,6 @@ export class Connector { switch (this.tabsStorage.getTab(tabID)?.type) { case 'gumby': return this.gumbyChatConnector.requestAnswer(tabID, payload) - case 'testgen': - return this.testChatConnector.requestAnswer(tabID, payload) } } @@ -210,10 +191,6 @@ export class Connector { new Promise((resolve, reject) => { if (this.isUIReady) { switch (this.tabsStorage.getTab(tabID)?.type) { - case 'featuredev': - return this.featureDevChatConnector.requestGenerativeAIAnswer(tabID, messageId, payload) - case 'doc': - return this.docChatConnector.requestGenerativeAIAnswer(tabID, messageId, payload) default: return this.cwChatConnector.requestGenerativeAIAnswer(tabID, messageId, payload) } @@ -247,10 +224,6 @@ export class Connector { } } - startTestGen = (tabID: string, prompt: string): void => { - this.testChatConnector.startTestGen(tabID, prompt) - } - transform = (tabID: string): void => { this.gumbyChatConnector.transform(tabID) } @@ -261,9 +234,6 @@ export class Connector { onStopChatResponse = (tabID: string): void => { switch (this.tabsStorage.getTab(tabID)?.type) { - case 'featuredev': - this.featureDevChatConnector.onStopChatResponse(tabID) - break case 'cwc': this.cwChatConnector.onStopChatResponse(tabID) break @@ -283,16 +253,10 @@ export class Connector { if (messageData.sender === 'CWChat') { await this.cwChatConnector.handleMessageReceive(messageData) - } else if (messageData.sender === 'featureDevChat') { - await this.featureDevChatConnector.handleMessageReceive(messageData) } else if (messageData.sender === 'gumbyChat') { await this.gumbyChatConnector.handleMessageReceive(messageData) } else if (messageData.sender === 'scanChat') { await this.scanChatConnector.handleMessageReceive(messageData) - } else if (messageData.sender === 'testChat') { - await this.testChatConnector.handleMessageReceive(messageData) - } else if (messageData.sender === 'docChat') { - await this.docChatConnector.handleMessageReceive(messageData) } else if (messageData.sender === 'amazonqCore') { await this.amazonqCommonsConnector.handleMessageReceive(messageData) } @@ -323,20 +287,6 @@ export class Connector { case 'review': this.scanChatConnector.onTabAdd(tabID) break - case 'testgen': - this.testChatConnector.onTabAdd(tabID) - break - } - } - - onKnownTabOpen = (tabID: string): void => { - switch (this.tabsStorage.getTab(tabID)?.type) { - case 'featuredev': - this.featureDevChatConnector.onTabOpen(tabID) - break - case 'doc': - this.docChatConnector.onTabOpen(tabID) - break } } @@ -372,23 +322,6 @@ export class Connector { codeBlockLanguage ) break - case 'featuredev': - this.featureDevChatConnector.onCodeInsertToCursorPosition( - tabID, - messageId, - code, - type, - codeReference, - eventId, - codeBlockIndex, - totalCodeBlocks, - userIntent, - codeBlockLanguage - ) - break - case 'testgen': - this.testChatConnector.onCodeInsertToCursorPosition(tabID, messageId, code, type, codeReference) - break } } @@ -477,20 +410,6 @@ export class Connector { codeBlockLanguage ) break - case 'featuredev': - this.featureDevChatConnector.onCopyCodeToClipboard( - tabID, - messageId, - code, - type, - codeReference, - eventId, - codeBlockIndex, - totalCodeBlocks, - userIntent, - codeBlockLanguage - ) - break } } @@ -501,21 +420,12 @@ export class Connector { case 'cwc': this.cwChatConnector.onTabRemove(tabID) break - case 'featuredev': - this.featureDevChatConnector.onTabRemove(tabID) - break - case 'doc': - this.docChatConnector.onTabRemove(tabID) - break case 'gumby': this.gumbyChatConnector.onTabRemove(tabID) break case 'review': this.scanChatConnector.onTabRemove(tabID) break - case 'testgen': - this.testChatConnector.onTabRemove(tabID) - break } } @@ -564,8 +474,6 @@ export class Connector { const tabType = this.tabsStorage.getTab(tabID)?.type switch (tabType) { case 'cwc': - case 'doc': - case 'featuredev': this.amazonqCommonsConnector.authFollowUpClicked(tabID, tabType, authType) } } @@ -578,49 +486,20 @@ export class Connector { case 'unknown': this.amazonqCommonsConnector.followUpClicked(tabID, followUp) break - case 'featuredev': - this.featureDevChatConnector.followUpClicked(tabID, messageId, followUp) - break - case 'testgen': - this.testChatConnector.followUpClicked(tabID, messageId, followUp) - break case 'review': this.scanChatConnector.followUpClicked(tabID, messageId, followUp) break - case 'doc': - this.docChatConnector.followUpClicked(tabID, messageId, followUp) - break default: this.cwChatConnector.followUpClicked(tabID, messageId, followUp) break } } - onFileActionClick = (tabID: string, messageId: string, filePath: string, actionName: string): void => { - switch (this.tabsStorage.getTab(tabID)?.type) { - case 'featuredev': - this.featureDevChatConnector.onFileActionClick(tabID, messageId, filePath, actionName) - break - case 'doc': - this.docChatConnector.onFileActionClick(tabID, messageId, filePath, actionName) - break - } - } - onFileClick = (tabID: string, filePath: string, deleted: boolean, messageId?: string): void => { switch (this.tabsStorage.getTab(tabID)?.type) { - case 'featuredev': - this.featureDevChatConnector.onOpenDiff(tabID, filePath, deleted, messageId) - break - case 'testgen': - this.testChatConnector.onFileDiff(tabID, filePath, deleted, messageId) - break case 'review': this.scanChatConnector.onFileClick(tabID, filePath, messageId) break - case 'doc': - this.docChatConnector.onOpenDiff(tabID, filePath, deleted) - break case 'cwc': this.cwChatConnector.onFileClick(tabID, filePath, messageId) break @@ -629,12 +508,6 @@ export class Connector { sendFeedback = (tabId: string, feedbackPayload: FeedbackPayload): void | undefined => { switch (this.tabsStorage.getTab(tabId)?.type) { - case 'featuredev': - this.featureDevChatConnector.sendFeedback(tabId, feedbackPayload) - break - case 'testgen': - this.testChatConnector.onSendFeedback(tabId, feedbackPayload) - break case 'cwc': this.cwChatConnector.onSendFeedback(tabId, feedbackPayload) break @@ -669,15 +542,9 @@ export class Connector { case 'cwc': this.cwChatConnector.onChatItemVoted(tabId, messageId, vote) break - case 'featuredev': - this.featureDevChatConnector.onChatItemVoted(tabId, messageId, vote) - break case 'review': this.scanChatConnector.onChatItemVoted(tabId, messageId, vote) break - case 'testgen': - this.testChatConnector.onChatItemVoted(tabId, messageId, vote) - break } } @@ -715,15 +582,9 @@ export class Connector { case 'gumby': this.gumbyChatConnector.onCustomFormAction(tabId, action) break - case 'testgen': - this.testChatConnector.onCustomFormAction(tabId, messageId ?? '', action) - break case 'review': this.scanChatConnector.onCustomFormAction(tabId, action) break - case 'doc': - this.docChatConnector.onCustomFormAction(tabId, action) - break case 'cwc': if (action.id === `open-settings`) { this.sendMessageToExtension({ diff --git a/packages/core/src/amazonq/webview/ui/connectorAdapter.ts b/packages/core/src/amazonq/webview/ui/connectorAdapter.ts index 1de1d8556c4..e645e74bd25 100644 --- a/packages/core/src/amazonq/webview/ui/connectorAdapter.ts +++ b/packages/core/src/amazonq/webview/ui/connectorAdapter.ts @@ -70,13 +70,7 @@ export class HybridChatAdapter implements ChatClientAdapter { } isSupportedQuickAction(command: string): boolean { - return ( - command === '/dev' || - command === '/test' || - command === '/review' || - command === '/doc' || - command === '/transform' - ) + return command === '/review' || command === '/transform' } handleQuickAction(prompt: ChatPrompt, tabId: string, eventId: string | undefined): void { @@ -85,11 +79,8 @@ export class HybridChatAdapter implements ChatClientAdapter { get initialQuickActions(): QuickActionCommandGroup[] { const tabDataGenerator = new TabDataGenerator({ - isDocEnabled: this.enableAgents, - isFeatureDevEnabled: this.enableAgents, isGumbyEnabled: this.enableAgents, isScanEnabled: this.enableAgents, - isTestEnabled: this.enableAgents, disabledCommands: this.disabledCommands, commandHighlight: this.featureConfigsSerialized.find(([name]) => name === 'highlightCommand')?.[1], }) diff --git a/packages/core/src/amazonq/webview/ui/followUps/generator.ts b/packages/core/src/amazonq/webview/ui/followUps/generator.ts index cce5726398f..a275cbaae6d 100644 --- a/packages/core/src/amazonq/webview/ui/followUps/generator.ts +++ b/packages/core/src/amazonq/webview/ui/followUps/generator.ts @@ -42,32 +42,6 @@ export class FollowUpGenerator { public generateWelcomeBlockForTab(tabType: TabType): FollowUpsBlock { switch (tabType) { - case 'featuredev': - return { - text: 'Ask a follow up question', - options: [ - { - pillText: 'What are some examples of tasks?', - type: 'DevExamples', - }, - ], - } - case 'doc': - return { - text: 'Select one of the following...', - options: [ - { - pillText: 'Create a README', - prompt: 'Create a README', - type: 'CreateDocumentation', - }, - { - pillText: 'Update an existing README', - prompt: 'Update an existing README', - type: 'UpdateDocumentation', - }, - ], - } default: return { text: 'Try Examples:', diff --git a/packages/core/src/amazonq/webview/ui/followUps/handler.ts b/packages/core/src/amazonq/webview/ui/followUps/handler.ts index 1fd38643827..6024a93ddee 100644 --- a/packages/core/src/amazonq/webview/ui/followUps/handler.ts +++ b/packages/core/src/amazonq/webview/ui/followUps/handler.ts @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ChatItemAction, ChatItemType, MynahIcons, MynahUI } from '@aws/mynah-ui' +import { ChatItemAction, ChatItemType, MynahUI } from '@aws/mynah-ui' import { Connector } from '../connector' import { TabsStorage } from '../storages/tabsStorage' import { WelcomeFollowupType } from '../apps/amazonqCommonsConnector' import { AuthFollowUpType } from './generator' -import { FollowUpTypes, MynahUIRef } from '../../../commons/types' +import { MynahUIRef } from '../../../commons/types' export interface FollowUpInteractionHandlerProps { mynahUIRef: MynahUIRef @@ -74,82 +74,6 @@ export class FollowUpInteractionHandler { } } - const addChatItem = (tabID: string, messageId: string, options: any[]) => { - this.mynahUI?.addChatItem(tabID, { - type: ChatItemType.ANSWER_PART, - messageId, - followUp: { - text: '', - options, - }, - }) - } - - const ViewDiffOptions = [ - { - icon: MynahIcons.OK, - pillText: 'Accept', - status: 'success', - type: FollowUpTypes.AcceptCode, - }, - { - icon: MynahIcons.REVERT, - pillText: 'Reject', - status: 'error', - type: FollowUpTypes.RejectCode, - }, - ] - - const AcceptCodeOptions = [ - { - icon: MynahIcons.OK, - pillText: 'Accepted', - status: 'success', - disabled: true, - }, - ] - - const RejectCodeOptions = [ - { - icon: MynahIcons.REVERT, - pillText: 'Rejected', - status: 'error', - disabled: true, - }, - ] - - const ViewCodeDiffAfterIterationOptions = [ - { - icon: MynahIcons.OK, - pillText: 'Accept', - status: 'success', - type: FollowUpTypes.AcceptCode, - }, - { - icon: MynahIcons.REVERT, - pillText: 'Reject', - status: 'error', - type: FollowUpTypes.RejectCode, // TODO: Add new Followup Action for "Reject" - }, - ] - - if (this.tabsStorage.getTab(tabID)?.type === 'testgen') { - switch (followUp.type) { - case FollowUpTypes.ViewDiff: - addChatItem(tabID, messageId, ViewDiffOptions) - break - case FollowUpTypes.AcceptCode: - addChatItem(tabID, messageId, AcceptCodeOptions) - break - case FollowUpTypes.RejectCode: - addChatItem(tabID, messageId, RejectCodeOptions) - break - case FollowUpTypes.ViewCodeDiffAfterIteration: - addChatItem(tabID, messageId, ViewCodeDiffAfterIterationOptions) - break - } - } - this.connector.onFollowUpClicked(tabID, messageId, followUp) } diff --git a/packages/core/src/amazonq/webview/ui/main.ts b/packages/core/src/amazonq/webview/ui/main.ts index 7d5bd48eaeb..54696982ae0 100644 --- a/packages/core/src/amazonq/webview/ui/main.ts +++ b/packages/core/src/amazonq/webview/ui/main.ts @@ -91,11 +91,8 @@ export class WebviewUIHandler { tabDataGenerator?: TabDataGenerator // are agents enabled - isFeatureDevEnabled: boolean isGumbyEnabled: boolean isScanEnabled: boolean - isTestEnabled: boolean - isDocEnabled: boolean isSMUS: boolean isSM: boolean @@ -166,21 +163,15 @@ export class WebviewUIHandler { }, }) - this.isFeatureDevEnabled = enableAgents this.isGumbyEnabled = enableAgents this.isScanEnabled = enableAgents - this.isTestEnabled = enableAgents - this.isDocEnabled = enableAgents this.featureConfigs = tryNewMap(featureConfigsSerialized) const highlightCommand = this.featureConfigs.get('highlightCommand') this.tabDataGenerator = new TabDataGenerator({ - isFeatureDevEnabled: enableAgents, isGumbyEnabled: enableAgents, isScanEnabled: enableAgents, - isTestEnabled: enableAgents, - isDocEnabled: enableAgents, disabledCommands, commandHighlight: highlightCommand, regionProfile, // TODO @@ -195,31 +186,22 @@ export class WebviewUIHandler { this.quickActionHandler?.handle(chatPrompt, tabId) }, onUpdateAuthentication: (isAmazonQEnabled: boolean, authenticatingTabIDs: string[]): void => { - this.isFeatureDevEnabled = isAmazonQEnabled this.isGumbyEnabled = isAmazonQEnabled this.isScanEnabled = isAmazonQEnabled - this.isTestEnabled = isAmazonQEnabled - this.isDocEnabled = isAmazonQEnabled this.quickActionHandler = new QuickActionHandler({ mynahUIRef: this.mynahUIRef, connector: this.connector!, tabsStorage: this.tabsStorage, - isFeatureDevEnabled: this.isFeatureDevEnabled, isGumbyEnabled: this.isGumbyEnabled, isScanEnabled: this.isScanEnabled, - isTestEnabled: this.isTestEnabled, - isDocEnabled: this.isDocEnabled, hybridChat, disabledCommands, }) this.tabDataGenerator = new TabDataGenerator({ - isFeatureDevEnabled: this.isFeatureDevEnabled, isGumbyEnabled: this.isGumbyEnabled, isScanEnabled: this.isScanEnabled, - isTestEnabled: this.isTestEnabled, - isDocEnabled: this.isDocEnabled, disabledCommands, commandHighlight: highlightCommand, regionProfile, // TODO @@ -244,8 +226,7 @@ export class WebviewUIHandler { if ( this.tabsStorage.getTab(tabID)?.type === 'gumby' || - this.tabsStorage.getTab(tabID)?.type === 'review' || - this.tabsStorage.getTab(tabID)?.type === 'testgen' + this.tabsStorage.getTab(tabID)?.type === 'review' ) { this.mynahUI?.updateStore(tabID, { promptInputDisabledState: false, @@ -567,7 +548,6 @@ export class WebviewUIHandler { return } this.tabsStorage.updateTabTypeFromUnknown(newTabID, tabType) - this.connector?.onKnownTabOpen(newTabID) this.connector?.onUpdateTabType(newTabID) this.mynahUI?.updateStore(newTabID, { @@ -716,11 +696,7 @@ export class WebviewUIHandler { } const tabType = this.tabsStorage.getTab(tabID)?.type - if (tabType === 'featuredev') { - this.mynahUI?.addChatItem(tabID, { - type: ChatItemType.ANSWER_STREAM, - }) - } else if (tabType === 'gumby') { + if (tabType === 'gumby') { this.connector?.requestAnswer(tabID, { chatMessage: prompt.prompt ?? '', }) @@ -973,9 +949,6 @@ export class WebviewUIHandler { onFollowUpClicked: (tabID, messageId, followUp) => { this.followUpsInteractionHandler?.onFollowUpClicked(tabID, messageId, followUp) }, - onFileActionClick: async (tabID: string, messageId: string, filePath: string, actionName: string) => { - this.connector?.onFileActionClick(tabID, messageId, filePath, actionName) - }, onFileClick: this.connector.onFileClick, tabs: { 'tab-1': { @@ -1037,11 +1010,8 @@ export class WebviewUIHandler { mynahUIRef: this.mynahUIRef, connector: this.connector, tabsStorage: this.tabsStorage, - isFeatureDevEnabled: this.isFeatureDevEnabled, isGumbyEnabled: this.isGumbyEnabled, isScanEnabled: this.isScanEnabled, - isTestEnabled: this.isTestEnabled, - isDocEnabled: this.isDocEnabled, hybridChat, }) this.textMessageHandler = new TextMessageHandler({ @@ -1053,11 +1023,8 @@ export class WebviewUIHandler { mynahUIRef: this.mynahUIRef, connector: this.connector, tabsStorage: this.tabsStorage, - isFeatureDevEnabled: this.isFeatureDevEnabled, isGumbyEnabled: this.isGumbyEnabled, isScanEnabled: this.isScanEnabled, - isTestEnabled: this.isTestEnabled, - isDocEnabled: this.isDocEnabled, }) } @@ -1102,12 +1069,6 @@ export class WebviewUIHandler { }, } } - // Show only "Copy" option for codeblocks in Q Test Tab - if (tab?.type === 'testgen') { - return { - 'insert-to-cursor': undefined, - } - } // Default will show "Copy" and "Insert at cursor" for codeblocks return {} } diff --git a/packages/core/src/amazonq/webview/ui/messages/controller.ts b/packages/core/src/amazonq/webview/ui/messages/controller.ts index a41d6a1f7f5..37a8077f8ae 100644 --- a/packages/core/src/amazonq/webview/ui/messages/controller.ts +++ b/packages/core/src/amazonq/webview/ui/messages/controller.ts @@ -14,11 +14,8 @@ export interface MessageControllerProps { mynahUIRef: MynahUIRef connector: Connector tabsStorage: TabsStorage - isFeatureDevEnabled: boolean isGumbyEnabled: boolean isScanEnabled: boolean - isTestEnabled: boolean - isDocEnabled: boolean disabledCommands?: string[] } @@ -33,11 +30,8 @@ export class MessageController { this.connector = props.connector this.tabsStorage = props.tabsStorage this.tabDataGenerator = new TabDataGenerator({ - isFeatureDevEnabled: props.isFeatureDevEnabled, isGumbyEnabled: props.isGumbyEnabled, isScanEnabled: props.isScanEnabled, - isTestEnabled: props.isTestEnabled, - isDocEnabled: props.isDocEnabled, disabledCommands: props.disabledCommands, }) } diff --git a/packages/core/src/amazonq/webview/ui/quickActions/generator.ts b/packages/core/src/amazonq/webview/ui/quickActions/generator.ts index 8500a04911d..0cc7740f2ec 100644 --- a/packages/core/src/amazonq/webview/ui/quickActions/generator.ts +++ b/packages/core/src/amazonq/webview/ui/quickActions/generator.ts @@ -8,28 +8,19 @@ import { TabType } from '../storages/tabsStorage' import { MynahIcons } from '@aws/mynah-ui' export interface QuickActionGeneratorProps { - isFeatureDevEnabled: boolean isGumbyEnabled: boolean isScanEnabled: boolean - isTestEnabled: boolean - isDocEnabled: boolean disableCommands?: string[] } export class QuickActionGenerator { - public isFeatureDevEnabled: boolean private isGumbyEnabled: boolean private isScanEnabled: boolean - private isTestEnabled: boolean - private isDocEnabled: boolean private disabledCommands: string[] constructor(props: QuickActionGeneratorProps) { - this.isFeatureDevEnabled = props.isFeatureDevEnabled this.isGumbyEnabled = props.isGumbyEnabled this.isScanEnabled = props.isScanEnabled - this.isTestEnabled = props.isTestEnabled - this.isDocEnabled = props.isDocEnabled this.disabledCommands = props.disableCommands ?? [] } @@ -43,7 +34,7 @@ export class QuickActionGenerator { const quickActionCommands = [ { commands: [ - ...(this.isFeatureDevEnabled && !this.disabledCommands.includes('/dev') + ...(!this.disabledCommands.includes('/dev') ? [ { command: '/dev', @@ -53,7 +44,7 @@ export class QuickActionGenerator { }, ] : []), - ...(this.isTestEnabled && !this.disabledCommands.includes('/test') + ...(!this.disabledCommands.includes('/test') ? [ { command: '/test', @@ -72,7 +63,7 @@ export class QuickActionGenerator { }, ] : []), - ...(this.isDocEnabled && !this.disabledCommands.includes('/doc') + ...(!this.disabledCommands.includes('/doc') ? [ { command: '/doc', @@ -120,10 +111,6 @@ export class QuickActionGenerator { description: '', unavailableItems: [], }, - featuredev: { - description: "This command isn't available in /dev", - unavailableItems: ['/help', '/clear'], - }, review: { description: "This command isn't available in /review", unavailableItems: ['/help', '/clear'], @@ -132,14 +119,6 @@ export class QuickActionGenerator { description: "This command isn't available in /transform", unavailableItems: ['/dev', '/test', '/doc', '/review', '/help', '/clear'], }, - testgen: { - description: "This command isn't available in /test", - unavailableItems: ['/help', '/clear'], - }, - doc: { - description: "This command isn't available in /doc", - unavailableItems: ['/help', '/clear'], - }, welcome: { description: '', unavailableItems: ['/clear'], diff --git a/packages/core/src/amazonq/webview/ui/quickActions/handler.ts b/packages/core/src/amazonq/webview/ui/quickActions/handler.ts index 6b017e419c0..ff23fb72635 100644 --- a/packages/core/src/amazonq/webview/ui/quickActions/handler.ts +++ b/packages/core/src/amazonq/webview/ui/quickActions/handler.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ChatItemType, ChatPrompt, MynahUI, NotificationType, MynahIcons } from '@aws/mynah-ui' +import { ChatItemType, ChatPrompt, MynahUI, NotificationType } from '@aws/mynah-ui' import { TabDataGenerator } from '../tabs/generator' import { Connector } from '../connector' import { TabsStorage, TabType } from '../storages/tabsStorage' @@ -14,11 +14,8 @@ export interface QuickActionsHandlerProps { mynahUIRef: { mynahUI: MynahUI | undefined } connector: Connector tabsStorage: TabsStorage - isFeatureDevEnabled: boolean isGumbyEnabled: boolean isScanEnabled: boolean - isTestEnabled: boolean - isDocEnabled: boolean hybridChat?: boolean disabledCommands?: string[] } @@ -36,30 +33,21 @@ export class QuickActionHandler { private connector: Connector private tabsStorage: TabsStorage private tabDataGenerator: TabDataGenerator - private isFeatureDevEnabled: boolean private isGumbyEnabled: boolean private isScanEnabled: boolean - private isTestEnabled: boolean - private isDocEnabled: boolean private isHybridChatEnabled: boolean constructor(props: QuickActionsHandlerProps) { this.mynahUIRef = props.mynahUIRef this.connector = props.connector this.tabsStorage = props.tabsStorage - this.isDocEnabled = props.isDocEnabled this.tabDataGenerator = new TabDataGenerator({ - isFeatureDevEnabled: props.isFeatureDevEnabled, isGumbyEnabled: props.isGumbyEnabled, isScanEnabled: props.isScanEnabled, - isTestEnabled: props.isTestEnabled, - isDocEnabled: props.isDocEnabled, disabledCommands: props.disabledCommands, }) - this.isFeatureDevEnabled = props.isFeatureDevEnabled this.isGumbyEnabled = props.isGumbyEnabled this.isScanEnabled = props.isScanEnabled - this.isTestEnabled = props.isTestEnabled this.isHybridChatEnabled = props.hybridChat ?? false } @@ -71,15 +59,6 @@ export class QuickActionHandler { public handle(chatPrompt: ChatPrompt, tabID: string, eventId?: string) { this.tabsStorage.resetTabTimer(tabID) switch (chatPrompt.command) { - case '/dev': - this.handleCommand({ - chatPrompt, - tabID, - taskName: 'Q - Dev', - tabType: 'featuredev', - isEnabled: this.isFeatureDevEnabled, - }) - break case '/help': this.handleHelpCommand(tabID) break @@ -89,18 +68,6 @@ export class QuickActionHandler { case '/review': this.handleScanCommand(tabID, eventId) break - case '/test': - this.handleTestCommand(chatPrompt, tabID, eventId) - break - case '/doc': - this.handleCommand({ - chatPrompt, - tabID, - taskName: 'Q - Doc', - tabType: 'doc', - isEnabled: this.isDocEnabled, - }) - break case '/clear': this.handleClearCommand(tabID) break @@ -145,7 +112,6 @@ export class QuickActionHandler { return } else { this.tabsStorage.updateTabTypeFromUnknown(affectedTabId, 'review') - this.connector.onKnownTabOpen(affectedTabId) this.connector.onUpdateTabType(affectedTabId) // reset chat history @@ -163,126 +129,6 @@ export class QuickActionHandler { } } - private handleTestCommand(chatPrompt: ChatPrompt, tabID: string | undefined, eventId: string | undefined) { - if (!this.isTestEnabled || !this.mynahUI) { - return - } - const testTabId = this.tabsStorage.getTabs().find((tab) => tab.type === 'testgen')?.id - const realPromptText = chatPrompt.escapedPrompt?.trim() ?? '' - - if (testTabId !== undefined) { - this.mynahUI.selectTab(testTabId, eventId || '') - this.connector.onTabChange(testTabId) - this.connector.startTestGen(testTabId, realPromptText) - return - } - - // if there is no test tab, open a new one - const affectedTabId: string | undefined = this.addTab(tabID) - - if (affectedTabId === undefined) { - this.mynahUI.notify({ - content: uiComponentsTexts.noMoreTabsTooltip, - type: NotificationType.WARNING, - }) - return - } else { - this.tabsStorage.updateTabTypeFromUnknown(affectedTabId, 'testgen') - this.connector.onKnownTabOpen(affectedTabId) - this.connector.onUpdateTabType(affectedTabId) - - // reset chat history - this.mynahUI.updateStore(affectedTabId, { - chatItems: [], - }) - - // creating a new tab and printing some title - this.mynahUI.updateStore( - affectedTabId, - this.tabDataGenerator.getTabData('testgen', realPromptText === '', 'Q - Test') - ) - - this.connector.startTestGen(affectedTabId, realPromptText) - } - } - - private handleCommand(props: HandleCommandProps) { - if (!props.isEnabled || !this.mynahUI) { - return - } - - const realPromptText = props.chatPrompt?.escapedPrompt?.trim() ?? '' - - const affectedTabId = this.addTab(props.tabID) - - if (affectedTabId === undefined) { - this.mynahUI.notify({ - content: uiComponentsTexts.noMoreTabsTooltip, - type: NotificationType.WARNING, - }) - return - } else { - this.tabsStorage.updateTabTypeFromUnknown(affectedTabId, props.tabType) - this.connector.onKnownTabOpen(affectedTabId) - this.connector.onUpdateTabType(affectedTabId) - - this.mynahUI.updateStore(affectedTabId, { chatItems: [] }) - - if (props.tabType === 'featuredev') { - this.mynahUI.updateStore( - affectedTabId, - this.tabDataGenerator.getTabData(props.tabType, false, props.taskName) - ) - } else { - this.mynahUI.updateStore( - affectedTabId, - this.tabDataGenerator.getTabData(props.tabType, realPromptText === '', props.taskName) - ) - } - - const addInformationCard = (tabId: string) => { - if (props.tabType === 'featuredev') { - this.mynahUI?.addChatItem(tabId, { - type: ChatItemType.ANSWER, - informationCard: { - title: 'Feature development', - description: 'Amazon Q Developer Agent for Software Development', - icon: MynahIcons.BUG, - content: { - body: [ - 'After you provide a task, I will:', - '1. Generate code based on your description and the code in your workspace', - '2. Provide a list of suggestions for you to review and add to your workspace', - '3. If needed, iterate based on your feedback', - 'To learn more, visit the [user guide](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/software-dev.html)', - ].join('\n'), - }, - }, - }) - } - } - if (realPromptText !== '') { - this.mynahUI.addChatItem(affectedTabId, { - type: ChatItemType.PROMPT, - body: realPromptText, - }) - addInformationCard(affectedTabId) - - this.mynahUI.updateStore(affectedTabId, { - loadingChat: true, - cancelButtonWhenLoading: false, - promptInputDisabledState: true, - }) - - void this.connector.requestGenerativeAIAnswer(affectedTabId, '', { - chatMessage: realPromptText, - }) - } else { - addInformationCard(affectedTabId) - } - } - } - private handleGumbyCommand(tabID: string, eventId: string | undefined) { if (!this.isGumbyEnabled || !this.mynahUI) { return @@ -319,7 +165,6 @@ export class QuickActionHandler { return } else { this.tabsStorage.updateTabTypeFromUnknown(affectedTabId, 'gumby') - this.connector.onKnownTabOpen(affectedTabId) this.connector.onUpdateTabType(affectedTabId) // reset chat history diff --git a/packages/core/src/amazonq/webview/ui/storages/tabsStorage.ts b/packages/core/src/amazonq/webview/ui/storages/tabsStorage.ts index f9a419fed96..92fa7c5a07e 100644 --- a/packages/core/src/amazonq/webview/ui/storages/tabsStorage.ts +++ b/packages/core/src/amazonq/webview/ui/storages/tabsStorage.ts @@ -4,17 +4,7 @@ */ export type TabStatus = 'free' | 'busy' | 'dead' -const TabTypes = [ - 'cwc', - 'featuredev', - 'gumby', - 'review', - 'testgen', - 'doc', - 'agentWalkthrough', - 'welcome', - 'unknown', -] as const +const TabTypes = ['cwc', 'gumby', 'review', 'agentWalkthrough', 'welcome', 'unknown'] as const export type TabType = (typeof TabTypes)[number] export function isTabType(value: string): value is TabType { return (TabTypes as readonly string[]).includes(value) @@ -22,16 +12,10 @@ export function isTabType(value: string): value is TabType { export function getTabCommandFromTabType(tabType: TabType): string { switch (tabType) { - case 'featuredev': - return '/dev' - case 'doc': - return '/doc' case 'gumby': return '/transform' case 'review': return '/review' - case 'testgen': - return '/test' default: return '' } diff --git a/packages/core/src/amazonq/webview/ui/tabs/constants.ts b/packages/core/src/amazonq/webview/ui/tabs/constants.ts index 9448f32d528..ead70679b7f 100644 --- a/packages/core/src/amazonq/webview/ui/tabs/constants.ts +++ b/packages/core/src/amazonq/webview/ui/tabs/constants.ts @@ -4,7 +4,6 @@ */ import { TabType } from '../storages/tabsStorage' import { QuickActionCommandGroup } from '@aws/mynah-ui' -import { userGuideURL } from '../texts/constants' const qChatIntroMessage = `Hi, I'm Amazon Q. I can answer your software development questions. Ask me to explain, debug, or optimize your code. @@ -48,18 +47,6 @@ export const commonTabData: TabTypeData = { export const TabTypeDataMap: Record, TabTypeData> = { unknown: commonTabData, cwc: commonTabData, - featuredev: { - title: 'Q - Dev', - placeholder: 'Describe your task or issue in as much detail as possible', - welcome: `I can generate code to accomplish a task or resolve an issue. - -After you provide a description, I will: -1. Generate code based on your description and the code in your workspace -2. Provide a list of suggestions for you to review and add to your workspace -3. If needed, iterate based on your feedback - -To learn more, visit the [User Guide](${userGuideURL}).`, - }, gumby: { title: 'Q - Code Transformation', placeholder: 'Open a new tab to chat with Q', @@ -71,16 +58,4 @@ To learn more, visit the [User Guide](${userGuideURL}).`, placeholder: `Ask a question or enter "/" for quick actions`, welcome: `Welcome to code reviews. I can help you identify code issues and provide suggested fixes for the active file or workspace you have opened in your IDE.`, }, - testgen: { - title: 'Q - Test', - placeholder: `Waiting on your inputs...`, - welcome: `Welcome to unit test generation. I can help you generate unit tests for your active file.`, - }, - doc: { - title: 'Q - Doc Generation', - placeholder: 'Ask Amazon Q to generate documentation for your project', - welcome: `Welcome to doc generation! - -I can help generate documentation for your code. To get started, choose what type of doc update you'd like to make.`, - }, } diff --git a/packages/core/src/amazonq/webview/ui/tabs/generator.ts b/packages/core/src/amazonq/webview/ui/tabs/generator.ts index 9698a9d8076..2331a0721c7 100644 --- a/packages/core/src/amazonq/webview/ui/tabs/generator.ts +++ b/packages/core/src/amazonq/webview/ui/tabs/generator.ts @@ -13,11 +13,8 @@ import { FeatureContext } from '../../../../shared/featureConfig' import { RegionProfile } from '../../../../codewhisperer/models/model' export interface TabDataGeneratorProps { - isFeatureDevEnabled: boolean isGumbyEnabled: boolean isScanEnabled: boolean - isTestEnabled: boolean - isDocEnabled: boolean disabledCommands?: string[] commandHighlight?: FeatureContext regionProfile?: RegionProfile @@ -32,11 +29,8 @@ export class TabDataGenerator { constructor(props: TabDataGeneratorProps) { this.followUpsGenerator = new FollowUpGenerator() this.quickActionsGenerator = new QuickActionGenerator({ - isFeatureDevEnabled: props.isFeatureDevEnabled, isGumbyEnabled: props.isGumbyEnabled, isScanEnabled: props.isScanEnabled, - isTestEnabled: props.isTestEnabled, - isDocEnabled: props.isDocEnabled, disableCommands: props.disabledCommands, }) this.highlightCommand = props.commandHighlight diff --git a/packages/core/src/amazonqDoc/app.ts b/packages/core/src/amazonqDoc/app.ts index 929cf1d45de..52985b82a00 100644 --- a/packages/core/src/amazonqDoc/app.ts +++ b/packages/core/src/amazonqDoc/app.ts @@ -6,7 +6,6 @@ import * as vscode from 'vscode' import { ChatControllerEventEmitters, DocController } from './controllers/chat/controller' import { AmazonQAppInitContext } from '../amazonq/apps/initContext' -import { MessagePublisher } from '../amazonq/messages/messagePublisher' import { MessageListener } from '../amazonq/messages/messageListener' import { fromQueryToParameters } from '../shared/utilities/uriUtils' import { getLogger } from '../shared/logger/logger' @@ -78,8 +77,6 @@ export function init(appContext: AmazonQAppInitContext) { webViewMessageListener: new MessageListener(docChatUIInputEventEmitter), }) - appContext.registerWebViewToAppMessagePublisher(new MessagePublisher(docChatUIInputEventEmitter), 'doc') - const debouncedEvent = debounce(async () => { const authenticated = (await AuthUtil.instance.getChatAuthState()).amazonQ === 'connected' let authenticatingSessionIDs: string[] = [] diff --git a/packages/core/src/amazonqFeatureDev/app.ts b/packages/core/src/amazonqFeatureDev/app.ts index a016d2ba481..fd0652fd0e4 100644 --- a/packages/core/src/amazonqFeatureDev/app.ts +++ b/packages/core/src/amazonqFeatureDev/app.ts @@ -7,7 +7,6 @@ import * as vscode from 'vscode' import { UIMessageListener } from './views/actions/uiMessageListener' import { ChatControllerEventEmitters, FeatureDevController } from './controllers/chat/controller' import { AmazonQAppInitContext } from '../amazonq/apps/initContext' -import { MessagePublisher } from '../amazonq/messages/messagePublisher' import { MessageListener } from '../amazonq/messages/messageListener' import { fromQueryToParameters } from '../shared/utilities/uriUtils' import { getLogger } from '../shared/logger/logger' @@ -81,11 +80,6 @@ export function init(appContext: AmazonQAppInitContext) { webViewMessageListener: new MessageListener(featureDevChatUIInputEventEmitter), }) - appContext.registerWebViewToAppMessagePublisher( - new MessagePublisher(featureDevChatUIInputEventEmitter), - 'featuredev' - ) - const debouncedEvent = debounce(async () => { const authenticated = (await AuthUtil.instance.getChatAuthState()).amazonQ === 'connected' let authenticatingSessionIDs: string[] = [] diff --git a/packages/core/src/amazonqTest/app.ts b/packages/core/src/amazonqTest/app.ts deleted file mode 100644 index 6c638c13b71..00000000000 --- a/packages/core/src/amazonqTest/app.ts +++ /dev/null @@ -1,76 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import { AmazonQAppInitContext } from '../amazonq/apps/initContext' -import { MessagePublisher } from '../amazonq/messages/messagePublisher' -import { MessageListener } from '../amazonq/messages/messageListener' -import { AuthUtil } from '../codewhisperer/util/authUtil' -import { ChatSessionManager } from './chat/storages/chatSession' -import { TestController, TestChatControllerEventEmitters } from './chat/controller/controller' -import { AppToWebViewMessageDispatcher } from './chat/views/connector/connector' -import { Messenger } from './chat/controller/messenger/messenger' -import { UIMessageListener } from './chat/views/actions/uiMessageListener' -import { debounce } from 'lodash' -import { testGenState } from '../codewhisperer/models/model' - -export function init(appContext: AmazonQAppInitContext) { - const testChatControllerEventEmitters: TestChatControllerEventEmitters = { - tabOpened: new vscode.EventEmitter(), - tabClosed: new vscode.EventEmitter(), - authClicked: new vscode.EventEmitter(), - startTestGen: new vscode.EventEmitter(), - processHumanChatMessage: new vscode.EventEmitter(), - updateTargetFileInfo: new vscode.EventEmitter(), - showCodeGenerationResults: new vscode.EventEmitter(), - openDiff: new vscode.EventEmitter(), - formActionClicked: new vscode.EventEmitter(), - followUpClicked: new vscode.EventEmitter(), - sendUpdatePromptProgress: new vscode.EventEmitter(), - errorThrown: new vscode.EventEmitter(), - insertCodeAtCursorPosition: new vscode.EventEmitter(), - processResponseBodyLinkClick: new vscode.EventEmitter(), - processChatItemVotedMessage: new vscode.EventEmitter(), - processChatItemFeedbackMessage: new vscode.EventEmitter(), - } - const dispatcher = new AppToWebViewMessageDispatcher(appContext.getAppsToWebViewMessagePublisher()) - const messenger = new Messenger(dispatcher) - - new TestController(testChatControllerEventEmitters, messenger, appContext.onDidChangeAmazonQVisibility.event) - - const testChatUIInputEventEmitter = new vscode.EventEmitter() - - new UIMessageListener({ - chatControllerEventEmitters: testChatControllerEventEmitters, - webViewMessageListener: new MessageListener(testChatUIInputEventEmitter), - }) - - appContext.registerWebViewToAppMessagePublisher(new MessagePublisher(testChatUIInputEventEmitter), 'testgen') - - const debouncedEvent = debounce(async () => { - const authenticated = (await AuthUtil.instance.getChatAuthState()).amazonQ === 'connected' - let authenticatingSessionID = '' - - if (authenticated) { - const session = ChatSessionManager.Instance.getSession() - - if (session.isTabOpen() && session.isAuthenticating) { - authenticatingSessionID = session.tabID! - session.isAuthenticating = false - } - } - - messenger.sendAuthenticationUpdate(authenticated, [authenticatingSessionID]) - }, 500) - - AuthUtil.instance.secondaryAuth.onDidChangeActiveConnection(() => { - return debouncedEvent() - }) - AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(() => { - return debouncedEvent() - }) - testGenState.setChatControllers(testChatControllerEventEmitters) - // TODO: Add testGen provider for creating new files after test generation if they does not exist -} diff --git a/packages/core/src/amazonqTest/chat/controller/controller.ts b/packages/core/src/amazonqTest/chat/controller/controller.ts deleted file mode 100644 index 747cca57e8e..00000000000 --- a/packages/core/src/amazonqTest/chat/controller/controller.ts +++ /dev/null @@ -1,1464 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - * - * This class is responsible for responding to UI events by calling - * the Test extension. - */ -import * as vscode from 'vscode' -import path from 'path' -import { FollowUps, Messenger, TestNamedMessages } from './messenger/messenger' -import { AuthController } from '../../../amazonq/auth/controller' -import { ChatSessionManager } from '../storages/chatSession' -import { BuildStatus, ConversationState, Session } from '../session/session' -import { AuthUtil } from '../../../codewhisperer/util/authUtil' -import { - buildProgressField, - cancellingProgressField, - cancelTestGenButton, - errorProgressField, - testGenBuildProgressMessage, - testGenCompletedField, - testGenProgressField, - testGenSummaryMessage, - maxUserPromptLength, -} from '../../models/constants' -import MessengerUtils, { ButtonActions } from './messenger/messengerUtils' -import { getTelemetryReasonDesc, isAwsError } from '../../../shared/errors' -import { ChatItemType } from '../../../amazonq/commons/model' -import { ChatItemButton, MynahIcons, ProgressField } from '@aws/mynah-ui' -import { FollowUpTypes } from '../../../amazonq/commons/types' -import { - cancelBuild, - runBuildCommand, - startTestGenerationProcess, -} from '../../../codewhisperer/commands/startTestGeneration' -import { UserIntent } from '@amzn/codewhisperer-streaming' -import { getSelectedCustomization } from '../../../codewhisperer/util/customizationUtil' -import { createCodeWhispererChatStreamingClient } from '../../../shared/clients/codewhispererChatClient' -import { - ChatItemVotedMessage, - ChatTriggerType, - TriggerPayload, -} from '../../../codewhispererChat/controllers/chat/model' -import { triggerPayloadToChatRequest } from '../../../codewhispererChat/controllers/chat/chatRequest/converter' -import { EditorContentController } from '../../../amazonq/commons/controllers/contentController' -import { amazonQTabSuffix } from '../../../shared/constants' -import { applyChanges } from '../../../shared/utilities/textDocumentUtilities' -import { telemetry } from '../../../shared/telemetry/telemetry' -import { CodeWhispererSettings } from '../../../codewhisperer/util/codewhispererSettings' -import globals from '../../../shared/extensionGlobals' -import { openUrl } from '../../../shared/utilities/vsCodeUtils' -import { getLogger } from '../../../shared/logger/logger' -import { i18n } from '../../../shared/i18n-helper' -import { sleep } from '../../../shared/utilities/timeoutUtils' -import { fs } from '../../../shared/fs/fs' -import { randomUUID } from '../../../shared/crypto' -import { tempDirPath, testGenerationLogsDir } from '../../../shared/filesystemUtilities' -import { CodeReference } from '../../../codewhispererChat/view/connector/connector' -import { TelemetryHelper } from '../../../codewhisperer/util/telemetryHelper' -import { Reference, testGenState } from '../../../codewhisperer/models/model' -import { - referenceLogText, - TestGenerationBuildStep, - tooManyRequestErrorMessage, - unitTestGenerationCancelMessage, - utgLimitReached, -} from '../../../codewhisperer/models/constants' -import { UserWrittenCodeTracker } from '../../../codewhisperer/tracker/userWrittenCodeTracker' -import { ReferenceLogViewProvider } from '../../../codewhisperer/service/referenceLogViewProvider' -import { TargetFileInfo } from '../../../codewhisperer/client/codewhispereruserclient' -import { submitFeedback } from '../../../feedback/vue/submitFeedback' -import { placeholder } from '../../../shared/vscode/commands2' -import { Auth } from '../../../auth/auth' -import { defaultContextLengths } from '../../../codewhispererChat/constants' - -export interface TestChatControllerEventEmitters { - readonly tabOpened: vscode.EventEmitter - readonly tabClosed: vscode.EventEmitter - readonly authClicked: vscode.EventEmitter - readonly startTestGen: vscode.EventEmitter - readonly processHumanChatMessage: vscode.EventEmitter - readonly updateTargetFileInfo: vscode.EventEmitter - readonly showCodeGenerationResults: vscode.EventEmitter - readonly openDiff: vscode.EventEmitter - readonly formActionClicked: vscode.EventEmitter - readonly followUpClicked: vscode.EventEmitter - readonly sendUpdatePromptProgress: vscode.EventEmitter - readonly errorThrown: vscode.EventEmitter - readonly insertCodeAtCursorPosition: vscode.EventEmitter - readonly processResponseBodyLinkClick: vscode.EventEmitter - readonly processChatItemVotedMessage: vscode.EventEmitter - readonly processChatItemFeedbackMessage: vscode.EventEmitter -} - -type OpenDiffMessage = { - tabID: string - messageId: string - filePath: string - codeGenerationId: string -} - -export class TestController { - private readonly messenger: Messenger - private readonly sessionStorage: ChatSessionManager - private authController: AuthController - private readonly editorContentController: EditorContentController - tempResultDirPath = path.join(tempDirPath, 'q-testgen') - - public constructor( - private readonly chatControllerMessageListeners: TestChatControllerEventEmitters, - messenger: Messenger, - onDidChangeAmazonQVisibility: vscode.Event - ) { - this.messenger = messenger - this.sessionStorage = ChatSessionManager.Instance - this.authController = new AuthController() - this.editorContentController = new EditorContentController() - - this.chatControllerMessageListeners.tabOpened.event((data) => { - return this.tabOpened(data) - }) - - this.chatControllerMessageListeners.tabClosed.event((data) => { - return this.tabClosed(data) - }) - - this.chatControllerMessageListeners.authClicked.event((data) => { - this.authClicked(data) - }) - - this.chatControllerMessageListeners.startTestGen.event(async (data) => { - await this.startTestGen(data, false) - }) - - this.chatControllerMessageListeners.processHumanChatMessage.event((data) => { - return this.processHumanChatMessage(data) - }) - - this.chatControllerMessageListeners.formActionClicked.event((data) => { - return this.handleFormActionClicked(data) - }) - - this.chatControllerMessageListeners.updateTargetFileInfo.event((data) => { - return this.updateTargetFileInfo(data) - }) - - this.chatControllerMessageListeners.showCodeGenerationResults.event((data) => { - return this.showCodeGenerationResults(data) - }) - - this.chatControllerMessageListeners.openDiff.event((data) => { - return this.openDiff(data) - }) - - this.chatControllerMessageListeners.sendUpdatePromptProgress.event((data) => { - return this.handleUpdatePromptProgress(data) - }) - - this.chatControllerMessageListeners.errorThrown.event((data) => { - return this.handleErrorMessage(data) - }) - - this.chatControllerMessageListeners.insertCodeAtCursorPosition.event((data) => { - return this.handleInsertCodeAtCursorPosition(data) - }) - - this.chatControllerMessageListeners.processResponseBodyLinkClick.event((data) => { - return this.processLink(data) - }) - - this.chatControllerMessageListeners.processChatItemVotedMessage.event((data) => { - this.processChatItemVotedMessage(data).catch((e) => { - getLogger().error('processChatItemVotedMessage failed: %s', (e as Error).message) - }) - }) - - this.chatControllerMessageListeners.processChatItemFeedbackMessage.event((data) => { - this.processChatItemFeedbackMessage(data).catch((e) => { - getLogger().error('processChatItemFeedbackMessage failed: %s', (e as Error).message) - }) - }) - - this.chatControllerMessageListeners.followUpClicked.event((data) => { - switch (data.followUp.type) { - case FollowUpTypes.ViewDiff: - return this.openDiff(data) - case FollowUpTypes.AcceptCode: - return this.acceptCode(data) - case FollowUpTypes.RejectCode: - return this.endSession(data, FollowUpTypes.RejectCode) - case FollowUpTypes.ContinueBuildAndExecute: - return this.handleBuildIteration(data) - case FollowUpTypes.BuildAndExecute: - return this.checkForInstallationDependencies(data) - case FollowUpTypes.ModifyCommands: - return this.modifyBuildCommand(data) - case FollowUpTypes.SkipBuildAndFinish: - return this.endSession(data, FollowUpTypes.SkipBuildAndFinish) - case FollowUpTypes.InstallDependenciesAndContinue: - return this.handleInstallDependencies(data) - case FollowUpTypes.ViewCodeDiffAfterIteration: - return this.openDiff(data) - } - }) - - AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(() => { - this.sessionStorage.removeActiveTab() - }) - } - - /** - * Basic Functions - */ - private async tabOpened(message: any) { - const session: Session = this.sessionStorage.getSession() - const tabID = this.sessionStorage.setActiveTab(message.tabID) - const logger = getLogger() - logger.debug('Tab opened Processing message tabId: %s', message.tabID) - - // check if authentication has expired - try { - logger.debug(`Q - Test: Session created with id: ${session.tabID}`) - - const authState = await AuthUtil.instance.getChatAuthState() - if (authState.amazonQ !== 'connected') { - void this.messenger.sendAuthNeededExceptionMessage(authState, tabID) - session.isAuthenticating = true - return - } - } catch (err: any) { - logger.error('tabOpened failed: %O', err) - this.messenger.sendErrorMessage(err.message, message.tabID) - } - } - - private async processChatItemVotedMessage(message: ChatItemVotedMessage) { - const session = this.sessionStorage.getSession() - - telemetry.amazonq_feedback.emit({ - featureId: 'amazonQTest', - amazonqConversationId: session.startTestGenerationRequestId, - credentialStartUrl: AuthUtil.instance.startUrl, - interactionType: message.vote, - }) - } - - private async processChatItemFeedbackMessage(message: any) { - const session = this.sessionStorage.getSession() - - await globals.telemetry.postFeedback({ - comment: `${JSON.stringify({ - type: 'testgen-chat-answer-feedback', - amazonqConversationId: session.startTestGenerationRequestId, - reason: message?.selectedOption, - userComment: message?.comment, - })}`, - sentiment: 'Negative', - }) - } - - private async tabClosed(data: any) { - getLogger().debug('Tab closed with data tab id: %s', data.tabID) - await this.sessionCleanUp() - getLogger().debug('Removing active tab') - this.sessionStorage.removeActiveTab() - } - - private authClicked(message: any) { - this.authController.handleAuth(message.authType) - - this.messenger.sendMessage('Follow instructions to re-authenticate ...', message.tabID, 'answer') - - // Explicitly ensure the user goes through the re-authenticate flow - this.messenger.sendChatInputEnabled(message.tabID, false) - } - - private processLink(message: any) { - void openUrl(vscode.Uri.parse(message.link)) - } - - private handleInsertCodeAtCursorPosition(message: any) { - this.editorContentController.insertTextAtCursorPosition(message.code, () => {}) - } - - private checkCodeDiffLengthAndBuildStatus(state: { codeDiffLength: number; buildStatus: BuildStatus }): boolean { - return state.codeDiffLength !== 0 && state.buildStatus !== BuildStatus.SUCCESS - } - - // Displaying error message to the user in the chat tab - private async handleErrorMessage(data: any) { - testGenState.setToNotStarted() - // eslint-disable-next-line unicorn/no-null - this.messenger.sendUpdatePromptProgress(data.tabID, null) - const session = this.sessionStorage.getSession() - const isCancel = data.error.uiMessage === unitTestGenerationCancelMessage - let telemetryErrorMessage = getTelemetryReasonDesc(data.error) - if (session.stopIteration) { - telemetryErrorMessage = getTelemetryReasonDesc(data.error.uiMessage.replaceAll('```', '')) - } - TelemetryHelper.instance.sendTestGenerationToolkitEvent( - session, - session.isSupportedLanguage, - true, - isCancel ? 'Cancelled' : 'Failed', - session.startTestGenerationRequestId, - performance.now() - session.testGenerationStartTime, - telemetryErrorMessage, - session.isCodeBlockSelected, - session.artifactsUploadDuration, - session.srcPayloadSize, - session.srcZipFileSize, - session.charsOfCodeAccepted, - session.numberOfTestsGenerated, - session.linesOfCodeGenerated, - session.charsOfCodeGenerated, - session.numberOfTestsGenerated, - session.linesOfCodeGenerated, - undefined, - isCancel ? 'CANCELLED' : 'FAILED' - ) - if (session.stopIteration) { - // Error from Science - this.messenger.sendMessage( - data.error.uiMessage.replaceAll('```', ''), - data.tabID, - 'answer', - 'testGenErrorMessage', - this.getFeedbackButtons() - ) - } else { - isCancel - ? this.messenger.sendMessage( - data.error.uiMessage, - data.tabID, - 'answer', - 'testGenErrorMessage', - this.getFeedbackButtons() - ) - : this.sendErrorMessage(data) - } - await this.sessionCleanUp() - return - } - // Client side error messages - private sendErrorMessage(data: { - tabID: string - error: { uiMessage: string; message: string; code: string; statusCode: string } - }) { - const { error, tabID } = data - - // If user reached monthly limit for builderId - if (error.code === 'CreateTestJobError') { - if (error.message.includes(utgLimitReached)) { - getLogger().error('Monthly quota reached for QSDA actions.') - return this.messenger.sendMessage( - i18n('AWS.amazonq.featureDev.error.monthlyLimitReached'), - tabID, - 'answer', - 'testGenErrorMessage', - this.getFeedbackButtons() - ) - } - if (error.message.includes('Too many requests')) { - getLogger().error(error.message) - return this.messenger.sendErrorMessage(tooManyRequestErrorMessage, tabID) - } - } - if (isAwsError(error)) { - if (error.code === 'ThrottlingException') { - // TODO: use the explicitly modeled exception reason for quota vs throttle{ - getLogger().error(error.message) - this.messenger.sendErrorMessage(tooManyRequestErrorMessage, tabID) - return - } - // other service errors: - // AccessDeniedException - should not happen because access is validated before this point in the client - // ValidationException - shouldn't happen because client should not send malformed requests - // ConflictException - should not happen because the client will maintain proper state - // InternalServerException - shouldn't happen but needs to be caught - getLogger().error('Other error message: %s', error.message) - this.messenger.sendErrorMessage('', tabID) - return - } - // other unexpected errors (TODO enumerate all other failure cases) - getLogger().error('Other error message: %s', error.uiMessage) - this.messenger.sendErrorMessage('', tabID) - } - - // This function handles actions if user clicked on any Button one of these cases will be executed - private async handleFormActionClicked(data: any) { - const typedAction = MessengerUtils.stringToEnumValue(ButtonActions, data.action as any) - let getFeedbackCommentData = '' - switch (typedAction) { - case ButtonActions.STOP_TEST_GEN: - testGenState.setToCancelling() - telemetry.ui_click.emit({ elementId: 'unitTestGeneration_cancelTestGenerationProgress' }) - break - case ButtonActions.STOP_BUILD: - cancelBuild() - void this.handleUpdatePromptProgress({ status: 'cancel', tabID: data.tabID }) - telemetry.ui_click.emit({ elementId: 'unitTestGeneration_cancelBuildProgress' }) - this.messenger.sendChatInputEnabled(data.tabID, true) - await this.sessionCleanUp() - break - case ButtonActions.PROVIDE_FEEDBACK: - getFeedbackCommentData = `Q Test Generation: RequestId: ${this.sessionStorage.getSession().startTestGenerationRequestId}, TestGenerationJobId: ${this.sessionStorage.getSession().testGenerationJob?.testGenerationJobId}` - void submitFeedback(placeholder, 'Amazon Q', getFeedbackCommentData) - telemetry.ui_click.emit({ elementId: 'unitTestGeneration_provideFeedback' }) - break - } - } - // This function handles actions if user gives any input from the chatInput box - private async processHumanChatMessage(data: { prompt: string; tabID: string }) { - const session = this.sessionStorage.getSession() - const conversationState = session.conversationState - - if (conversationState === ConversationState.WAITING_FOR_BUILD_COMMMAND_INPUT) { - this.messenger.sendChatInputEnabled(data.tabID, false) - this.sessionStorage.getSession().conversationState = ConversationState.IDLE - session.updatedBuildCommands = [data.prompt] - const updatedCommands = session.updatedBuildCommands.join('\n') - this.messenger.sendMessage(`Updated command to \`${updatedCommands}\``, data.tabID, 'prompt') - await this.checkForInstallationDependencies(data) - return - } else { - await this.startTestGen(data, false) - } - } - // This function takes filePath as input parameter and returns file language - private async getLanguageForFilePath(filePath: string): Promise { - try { - const document = await vscode.workspace.openTextDocument(filePath) - return document.languageId - } catch (error) { - return 'plaintext' - } - } - - private getFeedbackButtons(): ChatItemButton[] { - const buttons: ChatItemButton[] = [] - if (Auth.instance.isInternalAmazonUser()) { - buttons.push({ - keepCardAfterClick: true, - text: 'How can we make /test better?', - id: ButtonActions.PROVIDE_FEEDBACK, - disabled: false, // allow button to be re-clicked - position: 'outside', - icon: 'comment' as MynahIcons, - }) - } - return buttons - } - - /** - * Start Test Generation and show the code results - */ - - private async startTestGen(message: any, regenerateTests: boolean) { - const session: Session = this.sessionStorage.getSession() - // Perform session cleanup before start of unit test generation workflow unless there is an existing job in progress. - if (!ChatSessionManager.Instance.getIsInProgress()) { - await this.sessionCleanUp() - } - const tabID = this.sessionStorage.setActiveTab(message.tabID) - getLogger().debug('startTestGen message: %O', message) - getLogger().debug('startTestGen tabId: %O', message.tabID) - let fileName = '' - let filePath = '' - let userFacingMessage = '' - let userPrompt = '' - session.testGenerationStartTime = performance.now() - - try { - if (ChatSessionManager.Instance.getIsInProgress()) { - void vscode.window.showInformationMessage( - "There is already a test generation job in progress. Cancel current job or wait until it's finished to try again." - ) - return - } - if (testGenState.isCancelling()) { - void vscode.window.showInformationMessage( - 'There is a test generation job being cancelled. Please wait for cancellation to finish.' - ) - return - } - // Truncating the user prompt if the prompt is more than 4096. - userPrompt = message.prompt.slice(0, maxUserPromptLength) - - // check that the session is authenticated - const authState = await AuthUtil.instance.getChatAuthState() - if (authState.amazonQ !== 'connected') { - void this.messenger.sendAuthNeededExceptionMessage(authState, tabID) - session.isAuthenticating = true - return - } - - // check that a project/workspace is open - const workspaceFolders = vscode.workspace.workspaceFolders - if (workspaceFolders === undefined || workspaceFolders.length === 0) { - this.messenger.sendUnrecoverableErrorResponse('no-project-found', tabID) - return - } - - // check if IDE has active file open. - const activeEditor = vscode.window.activeTextEditor - // also check all open editors and allow this to proceed if only one is open (even if not main focus) - const allVisibleEditors = vscode.window.visibleTextEditors - const openFileEditors = allVisibleEditors.filter((editor) => editor.document.uri.scheme === 'file') - const hasOnlyOneOpenFileSplitView = openFileEditors.length === 1 - getLogger().debug(`hasOnlyOneOpenSplitView: ${hasOnlyOneOpenFileSplitView}`) - // is not a file if the currently highlighted window is not a file, and there is either more than one or no file windows open - const isNotFile = activeEditor?.document.uri.scheme !== 'file' && !hasOnlyOneOpenFileSplitView - getLogger().debug(`activeEditor: ${activeEditor}, isNotFile: ${isNotFile}`) - if (!activeEditor || isNotFile) { - this.messenger.sendUnrecoverableErrorResponse( - isNotFile ? 'invalid-file-type' : 'no-open-file-found', - tabID - ) - this.messenger.sendUpdatePlaceholder( - tabID, - 'Please open and highlight a source code file in order to generate tests.' - ) - this.messenger.sendChatInputEnabled(tabID, true) - this.sessionStorage.getSession().conversationState = ConversationState.WAITING_FOR_INPUT - return - } - - const fileEditorToTest = hasOnlyOneOpenFileSplitView ? openFileEditors[0] : activeEditor - getLogger().debug(`File path: ${fileEditorToTest.document.uri.fsPath}`) - filePath = fileEditorToTest.document.uri.fsPath - fileName = path.basename(filePath) - userFacingMessage = userPrompt - ? regenerateTests - ? `${userPrompt}` - : `/test ${userPrompt}` - : `/test Generate unit tests for \`${fileName}\`` - - session.hasUserPromptSupplied = userPrompt.length > 0 - - // displaying user message prompt in Test tab - this.messenger.sendMessage(userFacingMessage, tabID, 'prompt') - this.messenger.sendChatInputEnabled(tabID, false) - this.sessionStorage.getSession().conversationState = ConversationState.IN_PROGRESS - this.messenger.sendUpdatePromptProgress(message.tabID, testGenProgressField) - - const language = await this.getLanguageForFilePath(filePath) - session.fileLanguage = language - const workspaceFolder = vscode.workspace.getWorkspaceFolder(fileEditorToTest.document.uri) - - /* - For Re:Invent 2024 we are supporting only java and python for unit test generation, rest of the languages shows the similar experience as CWC - */ - if (!['java', 'python'].includes(language) || workspaceFolder === undefined) { - if (!workspaceFolder) { - // File is outside of workspace - const unsupportedMessage = `I can't generate tests for ${fileName} because the file is outside of workspace scope.
I can still provide examples, instructions and code suggestions.` - this.messenger.sendMessage(unsupportedMessage, tabID, 'answer') - } - // Keeping this metric as is. TODO - Change to true once we support through other feature - session.isSupportedLanguage = false - await this.onCodeGeneration( - session, - userPrompt, - tabID, - fileName, - filePath, - workspaceFolder !== undefined - ) - } else { - this.messenger.sendCapabilityCard({ tabID }) - this.messenger.sendMessage(testGenSummaryMessage(fileName), message.tabID, 'answer-part') - - // Grab the selection from the fileEditorToTest and get the vscode Range - const selection = fileEditorToTest.selection - let selectionRange = undefined - if ( - selection.start.line !== selection.end.line || - selection.start.character !== selection.end.character - ) { - selectionRange = new vscode.Range( - selection.start.line, - selection.start.character, - selection.end.line, - selection.end.character - ) - } - session.isCodeBlockSelected = selectionRange !== undefined - session.isSupportedLanguage = true - - /** - * Zip the project - * Create pre-signed URL and upload artifact to S3 - * send API request to startTestGeneration API - * Poll from getTestGeneration API - * Get Diff from exportResultArchive API - */ - ChatSessionManager.Instance.setIsInProgress(true) - await startTestGenerationProcess(filePath, message.prompt, tabID, true, selectionRange) - } - } catch (err: any) { - // TODO: refactor error handling to be more robust - ChatSessionManager.Instance.setIsInProgress(false) - getLogger().error('startTestGen failed: %O', err) - this.messenger.sendUpdatePromptProgress(message.tabID, cancellingProgressField) - this.sendErrorMessage({ tabID, error: err }) - this.messenger.sendChatInputEnabled(tabID, true) - this.sessionStorage.getSession().conversationState = ConversationState.WAITING_FOR_INPUT - await sleep(2000) - // eslint-disable-next-line unicorn/no-null - this.messenger.sendUpdatePromptProgress(message.tabID, null) - } - } - - // Updating Progress bar - private async handleUpdatePromptProgress(data: any) { - const getProgressField = (status: string): ProgressField | null => { - switch (status) { - case 'Completed': - return testGenCompletedField - case 'Error': - return errorProgressField - case 'cancel': - return cancellingProgressField - case 'InProgress': - default: - return { - status: 'info', - text: 'Generating unit tests...', - value: data.progressRate, - valueText: data.progressRate.toString() + '%', - actions: [cancelTestGenButton], - } - } - } - this.messenger.sendUpdatePromptProgress(data.tabID, getProgressField(data.status)) - - await sleep(2000) - - // don't flash the bar when generation in progress - if (data.status !== 'InProgress') { - // eslint-disable-next-line unicorn/no-null - this.messenger.sendUpdatePromptProgress(data.tabID, null) - } - } - - private async updateTargetFileInfo(message: { - tabID: string - targetFileInfo?: TargetFileInfo - testGenerationJobGroupName: string - testGenerationJobId: string - type: ChatItemType - filePath: string - }) { - this.messenger.sendShortSummary({ - type: 'answer', - tabID: message.tabID, - message: testGenSummaryMessage( - path.basename(message.targetFileInfo?.filePath ?? message.filePath), - message.targetFileInfo?.filePlan?.replaceAll('```', '') - ), - canBeVoted: true, - filePath: message.targetFileInfo?.testFilePath, - }) - } - - private async showCodeGenerationResults(data: { tabID: string; filePath: string; projectName: string }) { - const session = this.sessionStorage.getSession() - // return early if references are disabled and there are references - if (!CodeWhispererSettings.instance.isSuggestionsWithCodeReferencesEnabled() && session.references.length > 0) { - void vscode.window.showInformationMessage('Your settings do not allow code generation with references.') - await this.endSession(data, FollowUpTypes.SkipBuildAndFinish) - await this.sessionCleanUp() - return - } - const followUps: FollowUps = { - text: '', - options: [ - { - pillText: `View diff`, - type: FollowUpTypes.ViewDiff, - status: 'primary', - }, - ], - } - session.generatedFilePath = data.filePath - try { - const tempFilePath = path.join(this.tempResultDirPath, 'resultArtifacts', data.filePath) - const newContent = await fs.readFileText(tempFilePath) - const workspaceFolder = vscode.workspace.workspaceFolders?.[0] - let linesGenerated = newContent.split('\n').length - let charsGenerated = newContent.length - if (workspaceFolder) { - const projectPath = workspaceFolder.uri.fsPath - const absolutePath = path.join(projectPath, data.filePath) - const fileExists = await fs.existsFile(absolutePath) - if (fileExists) { - const originalContent = await fs.readFileText(absolutePath) - linesGenerated -= originalContent.split('\n').length - charsGenerated -= originalContent.length - } - } - session.linesOfCodeGenerated = linesGenerated > 0 ? linesGenerated : 0 - session.charsOfCodeGenerated = charsGenerated > 0 ? charsGenerated : 0 - } catch (e: any) { - getLogger().debug('failed to get chars and lines of code generated from test generation result: %O', e) - } - - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer', - codeGenerationId: '', - message: `${session.jobSummary}\n\n Please see the unit tests generated below. Click “View diff” to review the changes in the code editor.`, - canBeVoted: true, - messageId: '', - followUps, - fileList: { - fileTreeTitle: 'READY FOR REVIEW', - rootFolderTitle: data.projectName, - filePaths: [data.filePath], - }, - codeReference: session.references.map( - (ref: Reference) => - ({ - ...ref, - information: `${ref.licenseName} - ${ref.repository}`, - }) as CodeReference - ), - }) - this.messenger.sendChatInputEnabled(data.tabID, false) - this.messenger.sendUpdatePlaceholder(data.tabID, `Select View diff to see the generated unit tests.`) - this.sessionStorage.getSession().conversationState = ConversationState.IDLE - } - - private async openDiff(message: OpenDiffMessage) { - const session = this.sessionStorage.getSession() - const filePath = session.generatedFilePath - const absolutePath = path.join(session.projectRootPath, filePath) - const fileExists = await fs.existsFile(absolutePath) - const leftUri = fileExists ? vscode.Uri.file(absolutePath) : vscode.Uri.from({ scheme: 'untitled' }) - const rightUri = vscode.Uri.file(path.join(this.tempResultDirPath, 'resultArtifacts', filePath)) - const fileName = path.basename(absolutePath) - await vscode.commands.executeCommand('vscode.diff', leftUri, rightUri, `${fileName} ${amazonQTabSuffix}`) - telemetry.ui_click.emit({ elementId: 'unitTestGeneration_viewDiff' }) - session.latencyOfTestGeneration = performance.now() - session.testGenerationStartTime - this.messenger.sendUpdatePlaceholder(message.tabID, `Please select an action to proceed (Accept or Reject)`) - } - - private async acceptCode(message: any) { - const session = this.sessionStorage.getSession() - session.acceptedJobId = session.listOfTestGenerationJobId[session.listOfTestGenerationJobId.length - 1] - const filePath = session.generatedFilePath - const absolutePath = path.join(session.projectRootPath, filePath) - const fileExists = await fs.existsFile(absolutePath) - const buildCommand = session.updatedBuildCommands?.join(' ') - - const tempFilePath = path.join(this.tempResultDirPath, 'resultArtifacts', filePath) - const updatedContent = await fs.readFileText(tempFilePath) - let acceptedLines = updatedContent.split('\n').length - let acceptedChars = updatedContent.length - if (fileExists) { - const originalContent = await fs.readFileText(absolutePath) - acceptedLines -= originalContent.split('\n').length - acceptedLines = acceptedLines < 0 ? 0 : acceptedLines - acceptedChars -= originalContent.length - acceptedChars = acceptedChars < 0 ? 0 : acceptedChars - UserWrittenCodeTracker.instance.onQStartsMakingEdits() - const document = await vscode.workspace.openTextDocument(absolutePath) - await applyChanges( - document, - new vscode.Range(document.lineAt(0).range.start, document.lineAt(document.lineCount - 1).range.end), - updatedContent - ) - UserWrittenCodeTracker.instance.onQFinishesEdits() - } else { - await fs.writeFile(absolutePath, updatedContent) - } - session.charsOfCodeAccepted = acceptedChars - session.linesOfCodeAccepted = acceptedLines - - // add accepted references to reference log, if any - const fileName = path.basename(session.generatedFilePath) - const time = new Date().toLocaleString() - // TODO: this is duplicated in basicCommands.ts for scan (codewhisperer). Fix this later. - for (const reference of session.references) { - getLogger().debug('Processing reference: %O', reference) - // Log values for debugging - getLogger().debug('updatedContent: %s', updatedContent) - getLogger().debug( - 'start: %d, end: %d', - reference.recommendationContentSpan?.start, - reference.recommendationContentSpan?.end - ) - // given a start and end index, figure out which line number they belong to when splitting a string on /n characters - const getLineNumber = (content: string, index: number): number => { - const lines = content.slice(0, index).split('\n') - return lines.length - } - const startLine = getLineNumber(updatedContent, reference.recommendationContentSpan!.start) - const endLine = getLineNumber(updatedContent, reference.recommendationContentSpan!.end) - getLogger().debug('startLine: %d, endLine: %d', startLine, endLine) - - const code = updatedContent.slice( - reference.recommendationContentSpan?.start, - reference.recommendationContentSpan?.end - ) - getLogger().debug('Extracted code slice: %s', code) - const referenceLog = - `[${time}] Accepted recommendation ` + - referenceLogText( - `
${code}
`, - reference.licenseName!, - reference.repository!, - fileName, - startLine === endLine ? `(line at ${startLine})` : `(lines from ${startLine} to ${endLine})` - ) + - '
' - getLogger().debug('Adding reference log: %s', referenceLog) - ReferenceLogViewProvider.instance.addReferenceLog(referenceLog) - } - - // TODO: see if there's a better way to check if active file is a diff - if (vscode.window.tabGroups.activeTabGroup.activeTab?.label.includes(amazonQTabSuffix)) { - await vscode.commands.executeCommand('workbench.action.closeActiveEditor') - } - const document = await vscode.workspace.openTextDocument(absolutePath) - await vscode.window.showTextDocument(document) - // TODO: send the message once again once build is enabled - // this.messenger.sendMessage('Accepted', message.tabID, 'prompt') - telemetry.ui_click.emit({ elementId: 'unitTestGeneration_acceptDiff' }) - - getLogger().info( - `Generated unit tests are accepted for ${session.fileLanguage ?? 'plaintext'} language with jobId: ${session.listOfTestGenerationJobId[0]}, jobGroupName: ${session.testGenerationJobGroupName}, result: Succeeded` - ) - TelemetryHelper.instance.sendTestGenerationToolkitEvent( - session, - true, - true, - 'Succeeded', - session.startTestGenerationRequestId, - session.latencyOfTestGeneration, - undefined, - session.isCodeBlockSelected, - session.artifactsUploadDuration, - session.srcPayloadSize, - session.srcZipFileSize, - session.charsOfCodeAccepted, - session.numberOfTestsGenerated, - session.linesOfCodeAccepted, - session.charsOfCodeGenerated, - session.numberOfTestsGenerated, - session.linesOfCodeGenerated, - undefined, - 'ACCEPTED' - ) - - await this.endSession(message, FollowUpTypes.SkipBuildAndFinish) - return - - if (session.listOfTestGenerationJobId.length === 1) { - this.startInitialBuild(message) - this.messenger.sendChatInputEnabled(message.tabID, false) - } else if (session.listOfTestGenerationJobId.length < 4) { - const remainingIterations = 4 - session.listOfTestGenerationJobId.length - - let userMessage = 'Would you like Amazon Q to build and execute again, and fix errors?' - if (buildCommand) { - userMessage += ` I will be running this build command: \`${buildCommand}\`` - } - userMessage += `\nYou have ${remainingIterations} iteration${remainingIterations > 1 ? 's' : ''} left.` - - const followUps: FollowUps = { - text: '', - options: [ - { - pillText: `Rebuild`, - type: FollowUpTypes.ContinueBuildAndExecute, - status: 'primary', - }, - { - pillText: `Skip and finish`, - type: FollowUpTypes.SkipBuildAndFinish, - status: 'primary', - }, - ], - } - this.messenger.sendBuildProgressMessage({ - tabID: message.tabID, - messageType: 'answer', - codeGenerationId: '', - message: userMessage, - canBeVoted: false, - messageId: '', - followUps: followUps, - }) - this.messenger.sendChatInputEnabled(message.tabID, false) - } else { - this.sessionStorage.getSession().listOfTestGenerationJobId = [] - this.messenger.sendMessage( - 'You have gone through both iterations and this unit test generation workflow is complete.', - message.tabID, - 'answer' - ) - await this.sessionCleanUp() - } - await fs.delete(this.tempResultDirPath, { recursive: true }) - } - - /** - * Handle a regular incoming message when a user is in the code generation phase - */ - private async onCodeGeneration( - session: Session, - message: string, - tabID: string, - fileName: string, - filePath: string, - fileInWorkspace: boolean - ) { - try { - // TODO: Write this entire gen response to basiccommands and call here. - const editorText = await fs.readFileText(filePath) - - const triggerPayload: TriggerPayload = { - query: `Generate unit tests for the following part of my code: ${message?.trim() || fileName}`, - codeSelection: undefined, - trigger: ChatTriggerType.ChatMessage, - fileText: editorText, - fileLanguage: session.fileLanguage, - filePath: filePath, - message: `Generate unit tests for the following part of my code: ${message?.trim() || fileName}`, - matchPolicy: undefined, - codeQuery: undefined, - userIntent: UserIntent.GENERATE_UNIT_TESTS, - customization: getSelectedCustomization(), - profile: AuthUtil.instance.regionProfileManager.activeRegionProfile, - context: [], - relevantTextDocuments: [], - additionalContents: [], - documentReferences: [], - useRelevantDocuments: false, - contextLengths: { - ...defaultContextLengths, - }, - } - const chatRequest = triggerPayloadToChatRequest(triggerPayload) - const client = await createCodeWhispererChatStreamingClient() - const response = await client.generateAssistantResponse(chatRequest) - UserWrittenCodeTracker.instance.onQFeatureInvoked() - await this.messenger.sendAIResponse( - response, - session, - tabID, - randomUUID.toString(), - triggerPayload, - fileName, - fileInWorkspace - ) - } finally { - this.messenger.sendChatInputEnabled(tabID, true) - this.messenger.sendUpdatePlaceholder(tabID, `/test Generate unit tests...`) - this.sessionStorage.getSession().conversationState = ConversationState.WAITING_FOR_INPUT - } - } - - // TODO: Check if there are more cases to endSession if yes create a enum or type for step - private async endSession(data: any, step: FollowUpTypes) { - this.messenger.sendMessage( - 'Unit test generation completed.', - data.tabID, - 'answer', - 'testGenEndSessionMessage', - this.getFeedbackButtons() - ) - - const session = this.sessionStorage.getSession() - if (step === FollowUpTypes.RejectCode) { - TelemetryHelper.instance.sendTestGenerationToolkitEvent( - session, - true, - true, - 'Succeeded', - session.startTestGenerationRequestId, - session.latencyOfTestGeneration, - undefined, - session.isCodeBlockSelected, - session.artifactsUploadDuration, - session.srcPayloadSize, - session.srcZipFileSize, - 0, - 0, - 0, - session.charsOfCodeGenerated, - session.numberOfTestsGenerated, - session.linesOfCodeGenerated, - undefined, - 'REJECTED' - ) - telemetry.ui_click.emit({ elementId: 'unitTestGeneration_rejectDiff' }) - } - - await this.sessionCleanUp() - - // this.messenger.sendMessage(`Unit test generation workflow is completed.`, data.tabID, 'answer') - this.messenger.sendChatInputEnabled(data.tabID, true) - return - } - - /** - * BUILD LOOP IMPLEMENTATION - */ - - private startInitialBuild(data: any) { - // TODO: Remove the fallback build command after stable version of backend build command. - const userMessage = `Would you like me to help build and execute the test? I will need you to let me know what build command to run if you do.` - const followUps: FollowUps = { - text: '', - options: [ - { - pillText: `Specify command then build and execute`, - type: FollowUpTypes.ModifyCommands, - status: 'primary', - }, - { - pillText: `Skip and finish`, - type: FollowUpTypes.SkipBuildAndFinish, - status: 'primary', - }, - ], - } - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer', - codeGenerationId: '', - message: userMessage, - canBeVoted: false, - messageId: '', - followUps: followUps, - }) - this.messenger.sendChatInputEnabled(data.tabID, false) - } - - private async checkForInstallationDependencies(data: any) { - // const session: Session = this.sessionStorage.getSession() - // const listOfInstallationDependencies = session.testGenerationJob?.shortAnswer?.installationDependencies || [] - // MOCK: As there is no installation dependencies in shortAnswer - const listOfInstallationDependencies = [''] - const installationDependencies = listOfInstallationDependencies.join('\n') - - this.messenger.sendMessage('Build and execute', data.tabID, 'prompt') - telemetry.ui_click.emit({ elementId: 'unitTestGeneration_buildAndExecute' }) - - if (installationDependencies.length > 0) { - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer', - codeGenerationId: '', - message: `Looks like you don’t have ${listOfInstallationDependencies.length > 1 ? `these` : `this`} ${listOfInstallationDependencies.length} required package${listOfInstallationDependencies.length > 1 ? `s` : ``} installed.\n\`\`\`sh\n${installationDependencies}\n`, - canBeVoted: false, - messageId: '', - followUps: { - text: '', - options: [ - { - pillText: `Install and continue`, - type: FollowUpTypes.InstallDependenciesAndContinue, - status: 'primary', - }, - { - pillText: `Skip and finish`, - type: FollowUpTypes.SkipBuildAndFinish, - status: 'primary', - }, - ], - }, - }) - } else { - await this.startLocalBuildExecution(data) - } - } - - private async handleInstallDependencies(data: any) { - this.messenger.sendMessage('Installation dependencies and continue', data.tabID, 'prompt') - telemetry.ui_click.emit({ elementId: 'unitTestGeneration_installDependenciesAndContinue' }) - void this.startLocalBuildExecution(data) - } - - private async handleBuildIteration(data: any) { - this.messenger.sendMessage('Proceed with Iteration', data.tabID, 'prompt') - telemetry.ui_click.emit({ elementId: 'unitTestGeneration_proceedWithIteration' }) - await this.startLocalBuildExecution(data) - } - - private async startLocalBuildExecution(data: any) { - const session: Session = this.sessionStorage.getSession() - // const installationDependencies = session.shortAnswer?.installationDependencies ?? [] - // MOCK: ignoring the installation case until backend send response - const installationDependencies: string[] = [] - const buildCommands = session.updatedBuildCommands - if (!buildCommands) { - throw new Error('Build command not found') - return - } - - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.START_STEP), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - - this.messenger.sendUpdatePromptProgress(data.tabID, buildProgressField) - - if (installationDependencies.length > 0 && session.listOfTestGenerationJobId.length < 2) { - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.INSTALL_DEPENDENCIES, 'current'), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - - const status = await runBuildCommand(installationDependencies) - // TODO: Add separate status for installation dependencies - session.buildStatus = status - if (status === BuildStatus.FAILURE) { - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.INSTALL_DEPENDENCIES, 'error'), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - } - if (status === BuildStatus.CANCELLED) { - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.INSTALL_DEPENDENCIES, 'error'), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - this.messenger.sendMessage('Installation dependencies Cancelled', data.tabID, 'prompt') - this.messenger.sendMessage( - 'Unit test generation workflow is complete. You have 25 out of 30 Amazon Q Developer Agent invocations left this month.', - data.tabID, - 'answer' - ) - return - } - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.INSTALL_DEPENDENCIES, 'done'), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - } - - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.RUN_BUILD, 'current'), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - - const buildStatus = await runBuildCommand(buildCommands) - session.buildStatus = buildStatus - - if (buildStatus === BuildStatus.FAILURE) { - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.RUN_BUILD, 'error'), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - } else if (buildStatus === BuildStatus.CANCELLED) { - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.RUN_BUILD, 'error'), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - this.messenger.sendMessage('Build Cancelled', data.tabID, 'prompt') - this.messenger.sendMessage('Unit test generation workflow is complete.', data.tabID, 'answer') - return - } else { - // Build successful - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.RUN_BUILD, 'done'), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - } - - // Running execution tests - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.RUN_EXECUTION_TESTS, 'current'), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - // After running tests - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.RUN_EXECUTION_TESTS, 'done'), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - if (session.buildStatus !== BuildStatus.SUCCESS) { - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.FIXING_TEST_CASES, 'current'), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - await startTestGenerationProcess(session.sourceFilePath, '', data.tabID, false) - } - // TODO: Skip this if startTestGenerationProcess timeouts - if (session.generatedFilePath) { - await this.showTestCaseSummary(data) - } - } - - private async showTestCaseSummary(data: { tabID: string }) { - const session: Session = this.sessionStorage.getSession() - let codeDiffLength = 0 - if (session.buildStatus !== BuildStatus.SUCCESS) { - // Check the generated test file content, if fileContent length is 0, exit the unit test generation workflow. - const tempFilePath = path.join(this.tempResultDirPath, 'resultArtifacts', session.generatedFilePath) - const codeDiffFileContent = await fs.readFileText(tempFilePath) - codeDiffLength = codeDiffFileContent.length - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.FIXING_TEST_CASES + 1, 'done'), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - } - - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.PROCESS_TEST_RESULTS, 'current'), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.PROCESS_TEST_RESULTS, 'done'), - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - }) - - const followUps: FollowUps = { - text: '', - options: [ - { - pillText: `View diff`, - type: FollowUpTypes.ViewCodeDiffAfterIteration, - status: 'primary', - }, - ], - } - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer-part', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: testGenBuildProgressMessage(TestGenerationBuildStep.PROCESS_TEST_RESULTS + 1), - canBeVoted: true, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - followUps: undefined, - fileList: this.checkCodeDiffLengthAndBuildStatus({ codeDiffLength, buildStatus: session.buildStatus }) - ? { - fileTreeTitle: 'READY FOR REVIEW', - rootFolderTitle: 'tests', - filePaths: [session.generatedFilePath], - } - : undefined, - }) - this.messenger.sendBuildProgressMessage({ - tabID: data.tabID, - messageType: 'answer', - codeGenerationId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - message: undefined, - canBeVoted: false, - messageId: TestNamedMessages.TEST_GENERATION_BUILD_STATUS_MESSAGE, - followUps: this.checkCodeDiffLengthAndBuildStatus({ codeDiffLength, buildStatus: session.buildStatus }) - ? followUps - : undefined, - fileList: undefined, - }) - - this.messenger.sendUpdatePromptProgress(data.tabID, testGenCompletedField) - await sleep(2000) - // eslint-disable-next-line unicorn/no-null - this.messenger.sendUpdatePromptProgress(data.tabID, null) - this.messenger.sendChatInputEnabled(data.tabID, false) - - if (codeDiffLength === 0 || session.buildStatus === BuildStatus.SUCCESS) { - this.messenger.sendMessage('Unit test generation workflow is complete.', data.tabID, 'answer') - await this.sessionCleanUp() - } - } - - private modifyBuildCommand(data: any) { - this.sessionStorage.getSession().conversationState = ConversationState.WAITING_FOR_BUILD_COMMMAND_INPUT - this.messenger.sendMessage('Specify commands then build', data.tabID, 'prompt') - telemetry.ui_click.emit({ elementId: 'unitTestGeneration_modifyCommand' }) - this.messenger.sendMessage( - 'Sure, provide all command lines you’d like me to run to build.', - data.tabID, - 'answer' - ) - this.messenger.sendUpdatePlaceholder(data.tabID, 'Waiting on your Inputs') - this.messenger.sendChatInputEnabled(data.tabID, true) - } - - /** Perform Session CleanUp in below cases - * UTG success - * End Session with Reject or SkipAndFinish - * After finishing 3 build loop iterations - * Error while generating unit tests - * Closing a Q-Test tab - * Progress bar cancel - */ - private async sessionCleanUp() { - const session = this.sessionStorage.getSession() - const groupName = session.testGenerationJobGroupName - const filePath = session.generatedFilePath - getLogger().debug('Entering sessionCleanUp function with filePath: %s and groupName: %s', filePath, groupName) - - vscode.window.tabGroups.all.flatMap(({ tabs }) => - tabs.map((tab) => { - if (tab.label === `${path.basename(filePath)} ${amazonQTabSuffix}`) { - const tabClosed = vscode.window.tabGroups.close(tab) - if (!tabClosed) { - getLogger().error('ChatDiff: Unable to close the diff view tab for %s', tab.label) - } - } - }) - ) - - getLogger().debug( - 'listOfTestGenerationJobId length: %d, groupName: %s', - session.listOfTestGenerationJobId.length, - groupName - ) - if (session.listOfTestGenerationJobId.length && groupName) { - for (const id of session.listOfTestGenerationJobId) { - if (id === session.acceptedJobId) { - TelemetryHelper.instance.sendTestGenerationEvent( - groupName, - id, - session.fileLanguage, - session.numberOfTestsGenerated, - session.numberOfTestsGenerated, // this is number of accepted test cases, now they can only accept all - session.linesOfCodeGenerated, - session.linesOfCodeAccepted, - session.charsOfCodeGenerated, - session.charsOfCodeAccepted - ) - } else { - TelemetryHelper.instance.sendTestGenerationEvent( - groupName, - id, - session.fileLanguage, - session.numberOfTestsGenerated, - 0, - session.linesOfCodeGenerated, - 0, - session.charsOfCodeGenerated, - 0 - ) - } - } - } - session.listOfTestGenerationJobId = [] - session.testGenerationJobGroupName = undefined - // session.testGenerationJob = undefined - session.updatedBuildCommands = undefined - session.shortAnswer = undefined - session.testCoveragePercentage = 0 - session.conversationState = ConversationState.IDLE - session.sourceFilePath = '' - session.generatedFilePath = '' - session.projectRootPath = '' - session.stopIteration = false - session.fileLanguage = undefined - ChatSessionManager.Instance.setIsInProgress(false) - session.linesOfCodeGenerated = 0 - session.linesOfCodeAccepted = 0 - session.charsOfCodeGenerated = 0 - session.charsOfCodeAccepted = 0 - session.acceptedJobId = '' - session.numberOfTestsGenerated = 0 - if (session.tabID) { - getLogger().debug('Setting input state with tabID: %s', session.tabID) - this.messenger.sendChatInputEnabled(session.tabID, true) - this.messenger.sendUpdatePlaceholder(session.tabID, 'Enter "/" for quick actions') - } - getLogger().debug( - 'Deleting output.log and temp result directory. testGenerationLogsDir: %s', - testGenerationLogsDir - ) - const outputLogPath = path.join(testGenerationLogsDir, 'output.log') - if (await fs.existsFile(outputLogPath)) { - await fs.delete(outputLogPath) - } - if ( - await fs - .stat(this.tempResultDirPath) - .then(() => true) - .catch(() => false) - ) { - await fs.delete(this.tempResultDirPath, { recursive: true }) - } - } - - // TODO: return build command when product approves - // private getBuildCommands = (): string[] => { - // const session = this.sessionStorage.getSession() - // if (session.updatedBuildCommands?.length) { - // return [...session.updatedBuildCommands] - // } - - // // For Internal amazon users only - // if (Auth.instance.isInternalAmazonUser()) { - // return ['brazil-build release'] - // } - - // if (session.shortAnswer && Array.isArray(session.shortAnswer?.buildCommands)) { - // return [...session.shortAnswer.buildCommands] - // } - - // return ['source qdev-wbr/.venv/bin/activate && pytest --continue-on-collection-errors'] - // } -} diff --git a/packages/core/src/amazonqTest/chat/controller/messenger/messenger.ts b/packages/core/src/amazonqTest/chat/controller/messenger/messenger.ts deleted file mode 100644 index 5541ef389c5..00000000000 --- a/packages/core/src/amazonqTest/chat/controller/messenger/messenger.ts +++ /dev/null @@ -1,365 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - * - * This class controls the presentation of the various chat bubbles presented by the - * Q Test. - * - * As much as possible, all strings used in the experience should originate here. - */ - -import { AuthFollowUpType, AuthMessageDataMap } from '../../../../amazonq/auth/model' -import { FeatureAuthState } from '../../../../codewhisperer/util/authUtil' -import { - AppToWebViewMessageDispatcher, - AuthNeededException, - AuthenticationUpdateMessage, - BuildProgressMessage, - CapabilityCardMessage, - ChatInputEnabledMessage, - ChatMessage, - ChatSummaryMessage, - ErrorMessage, - UpdatePlaceholderMessage, - UpdatePromptProgressMessage, -} from '../../views/connector/connector' -import { ChatItemType } from '../../../../amazonq/commons/model' -import { ChatItemAction, ChatItemButton, ProgressField } from '@aws/mynah-ui' -import * as CodeWhispererConstants from '../../../../codewhisperer/models/constants' -import { TriggerPayload } from '../../../../codewhispererChat/controllers/chat/model' -import { - CodeWhispererStreamingServiceException, - GenerateAssistantResponseCommandOutput, -} from '@amzn/codewhisperer-streaming' -import { Session } from '../../session/session' -import { CodeReference } from '../../../../amazonq/webview/ui/apps/amazonqCommonsConnector' -import { getHttpStatusCode, getRequestId, getTelemetryReasonDesc, ToolkitError } from '../../../../shared/errors' -import { sleep, waitUntil } from '../../../../shared/utilities/timeoutUtils' -import { keys } from '../../../../shared/utilities/tsUtils' -import { cancellingProgressField, testGenCompletedField } from '../../../models/constants' -import { testGenState } from '../../../../codewhisperer/models/model' -import { TelemetryHelper } from '../../../../codewhisperer/util/telemetryHelper' - -export type UnrecoverableErrorType = 'no-project-found' | 'no-open-file-found' | 'invalid-file-type' - -export enum TestNamedMessages { - TEST_GENERATION_BUILD_STATUS_MESSAGE = 'testGenerationBuildStatusMessage', -} - -export interface FollowUps { - text?: string - options?: ChatItemAction[] -} - -export interface FileList { - fileTreeTitle?: string - rootFolderTitle?: string - filePaths?: string[] -} - -export interface SendBuildProgressMessageParams { - tabID: string - messageType: ChatItemType - codeGenerationId: string - message?: string - canBeVoted: boolean - messageId?: string - followUps?: FollowUps - fileList?: FileList - codeReference?: CodeReference[] -} - -export class Messenger { - public constructor(private readonly dispatcher: AppToWebViewMessageDispatcher) {} - - public sendCapabilityCard(params: { tabID: string }) { - this.dispatcher.sendChatMessage(new CapabilityCardMessage(params.tabID)) - } - - public sendMessage( - message: string, - tabID: string, - messageType: ChatItemType, - messageId?: string, - buttons?: ChatItemButton[] - ) { - this.dispatcher.sendChatMessage( - new ChatMessage({ message, messageType, messageId: messageId, buttons: buttons }, tabID) - ) - } - - public sendShortSummary(params: { - message?: string - type: ChatItemType - tabID: string - messageID?: string - canBeVoted?: boolean - filePath?: string - }) { - this.dispatcher.sendChatSummaryMessage( - new ChatSummaryMessage( - { - message: params.message, - messageType: params.type, - messageId: params.messageID, - canBeVoted: params.canBeVoted, - filePath: params.filePath, - }, - params.tabID - ) - ) - } - - public sendChatInputEnabled(tabID: string, enabled: boolean) { - this.dispatcher.sendChatInputEnabled(new ChatInputEnabledMessage(tabID, enabled)) - } - - public sendUpdatePlaceholder(tabID: string, newPlaceholder: string) { - this.dispatcher.sendUpdatePlaceholder(new UpdatePlaceholderMessage(tabID, newPlaceholder)) - } - - public sendUpdatePromptProgress(tabID: string, progressField: ProgressField | null) { - this.dispatcher.sendUpdatePromptProgress(new UpdatePromptProgressMessage(tabID, progressField)) - } - - public async sendAuthNeededExceptionMessage(credentialState: FeatureAuthState, tabID: string) { - let authType: AuthFollowUpType = 'full-auth' - let message = AuthMessageDataMap[authType].message - - switch (credentialState.amazonQ) { - case 'disconnected': - authType = 'full-auth' - message = AuthMessageDataMap[authType].message - break - case 'unsupported': - authType = 'use-supported-auth' - message = AuthMessageDataMap[authType].message - break - case 'expired': - authType = 're-auth' - message = AuthMessageDataMap[authType].message - break - } - - this.dispatcher.sendAuthNeededExceptionMessage(new AuthNeededException(message, authType, tabID)) - } - - public sendAuthenticationUpdate(testEnabled: boolean, authenticatingTabIDs: string[]) { - this.dispatcher.sendAuthenticationUpdate(new AuthenticationUpdateMessage(testEnabled, authenticatingTabIDs)) - } - - /** - * This method renders an error message with a button at the end that will try the - * transformation again from the beginning. This message is meant for errors that are - * completely unrecoverable: the job cannot be completed in its current state, - * and the flow must be tried again. - */ - public sendUnrecoverableErrorResponse(type: UnrecoverableErrorType, tabID: string) { - let message = '...' - switch (type) { - case 'no-project-found': - message = CodeWhispererConstants.noOpenProjectsFoundChatTestGenMessage - break - case 'no-open-file-found': - message = CodeWhispererConstants.noOpenFileFoundChatMessage - break - case 'invalid-file-type': - message = CodeWhispererConstants.invalidFileTypeChatMessage - break - } - this.sendMessage(message, tabID, 'answer-stream') - } - - public sendErrorMessage(errorMessage: string, tabID: string) { - this.dispatcher.sendErrorMessage( - new ErrorMessage(CodeWhispererConstants.genericErrorMessage, errorMessage, tabID) - ) - } - - // To show the response of unsupported languages to the user in the Q-Test tab - public async sendAIResponse( - response: GenerateAssistantResponseCommandOutput, - session: Session, - tabID: string, - triggerID: string, - triggerPayload: TriggerPayload, - fileName: string, - fileInWorkspace: boolean - ) { - let message = '' - let messageId = response.$metadata.requestId ?? '' - let codeReference: CodeReference[] = [] - - if (response.generateAssistantResponseResponse === undefined) { - throw new ToolkitError( - `Empty response from Q Developer service. Request ID: ${response.$metadata.requestId}` - ) - } - - const eventCounts = new Map() - waitUntil( - async () => { - for await (const chatEvent of response.generateAssistantResponseResponse!) { - for (const key of keys(chatEvent)) { - if ((chatEvent[key] as any) !== undefined) { - eventCounts.set(key, (eventCounts.get(key) ?? 0) + 1) - } - } - - if ( - chatEvent.codeReferenceEvent?.references !== undefined && - chatEvent.codeReferenceEvent.references.length > 0 - ) { - codeReference = [ - ...codeReference, - ...chatEvent.codeReferenceEvent.references.map((reference) => ({ - ...reference, - recommendationContentSpan: { - start: reference.recommendationContentSpan?.start ?? 0, - end: reference.recommendationContentSpan?.end ?? 0, - }, - information: `Reference code under **${reference.licenseName}** license from repository \`${reference.repository}\``, - })), - ] - } - if (testGenState.isCancelling()) { - return true - } - if ( - chatEvent.assistantResponseEvent?.content !== undefined && - chatEvent.assistantResponseEvent.content.length > 0 - ) { - message += chatEvent.assistantResponseEvent.content - this.dispatcher.sendBuildProgressMessage( - new BuildProgressMessage({ - tabID, - messageType: 'answer-part', - codeGenerationId: '', - message, - canBeVoted: false, - messageId, - followUps: undefined, - fileList: undefined, - }) - ) - } - } - return true - }, - { timeout: 60000, truthy: true } - ) - .catch((error: any) => { - let errorMessage = 'Error reading chat stream.' - let statusCode = undefined - let requestID = undefined - if (error instanceof CodeWhispererStreamingServiceException) { - errorMessage = error.message - statusCode = getHttpStatusCode(error) ?? 0 - requestID = getRequestId(error) - } - let message = 'This error is reported to the team automatically. Please try sending your message again.' - if (errorMessage !== undefined) { - message += `\n\nDetails: ${errorMessage}` - } - - if (statusCode !== undefined) { - message += `\n\nStatus Code: ${statusCode}` - } - - if (requestID !== undefined) { - messageId = requestID - message += `\n\nRequest ID: ${requestID}` - } - this.sendMessage(message.trim(), tabID, 'answer') - }) - .finally(async () => { - if (testGenState.isCancelling()) { - this.sendMessage(CodeWhispererConstants.unitTestGenerationCancelMessage, tabID, 'answer') - TelemetryHelper.instance.sendTestGenerationToolkitEvent( - session, - false, - fileInWorkspace, - 'Cancelled', - messageId, - performance.now() - session.testGenerationStartTime, - getTelemetryReasonDesc( - `TestGenCancelled: ${CodeWhispererConstants.unitTestGenerationCancelMessage}` - ), - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - 'TestGenCancelled', - 'CANCELLED' - ) - this.dispatcher.sendUpdatePromptProgress( - new UpdatePromptProgressMessage(tabID, cancellingProgressField) - ) - await sleep(500) - } else { - TelemetryHelper.instance.sendTestGenerationToolkitEvent( - session, - false, - fileInWorkspace, - 'Succeeded', - messageId, - performance.now() - session.testGenerationStartTime, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - 'ACCEPTED' - ) - this.dispatcher.sendUpdatePromptProgress( - new UpdatePromptProgressMessage(tabID, testGenCompletedField) - ) - await sleep(500) - } - testGenState.setToNotStarted() - // eslint-disable-next-line unicorn/no-null - this.dispatcher.sendUpdatePromptProgress(new UpdatePromptProgressMessage(tabID, null)) - }) - } - - // To show the Build progress in the chat - public sendBuildProgressMessage(params: SendBuildProgressMessageParams) { - const { - tabID, - messageType, - codeGenerationId, - message, - canBeVoted, - messageId, - followUps, - fileList, - codeReference, - } = params - this.dispatcher.sendBuildProgressMessage( - new BuildProgressMessage({ - tabID, - messageType, - codeGenerationId, - message, - canBeVoted, - messageId, - followUps, - fileList, - codeReference, - }) - ) - } -} diff --git a/packages/core/src/amazonqTest/chat/controller/messenger/messengerUtils.ts b/packages/core/src/amazonqTest/chat/controller/messenger/messengerUtils.ts deleted file mode 100644 index 1eecc0aa4cd..00000000000 --- a/packages/core/src/amazonqTest/chat/controller/messenger/messengerUtils.ts +++ /dev/null @@ -1,31 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - * - */ - -// These enums map to string IDs -export enum ButtonActions { - ACCEPT = 'Accept', - MODIFY = 'Modify', - REJECT = 'Reject', - VIEW_DIFF = 'View-Diff', - STOP_TEST_GEN = 'Stop-Test-Generation', - STOP_BUILD = 'Stop-Build-Process', - PROVIDE_FEEDBACK = 'Provide-Feedback', -} - -// TODO: Refactor the common functionality between Transform, FeatureDev, CWSPRChat, Scan and UTG to a new Folder. - -export default class MessengerUtils { - static stringToEnumValue = ( - enumObject: T, - value: `${T[K]}` - ): T[K] => { - if (Object.values(enumObject).includes(value)) { - return value as unknown as T[K] - } else { - throw new Error('Value provided was not found in Enum') - } - } -} diff --git a/packages/core/src/amazonqTest/chat/session/session.ts b/packages/core/src/amazonqTest/chat/session/session.ts deleted file mode 100644 index 4e3780e6f99..00000000000 --- a/packages/core/src/amazonqTest/chat/session/session.ts +++ /dev/null @@ -1,77 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { ShortAnswer, Reference } from '../../../codewhisperer/models/model' -import { TargetFileInfo, TestGenerationJob } from '../../../codewhisperer/client/codewhispereruserclient' - -export enum ConversationState { - IDLE, - JOB_SUBMITTED, - WAITING_FOR_INPUT, - WAITING_FOR_BUILD_COMMMAND_INPUT, - WAITING_FOR_REGENERATE_INPUT, - IN_PROGRESS, -} - -export enum BuildStatus { - SUCCESS, - FAILURE, - CANCELLED, -} - -export class Session { - // Used to keep track of whether or not the current session is currently authenticating/needs authenticating - public isAuthenticating: boolean = false - - // A tab may or may not be currently open - public tabID: string | undefined - - // This is unique per each test generation cycle - public testGenerationJobGroupName: string | undefined = undefined - public listOfTestGenerationJobId: string[] = [] - public startTestGenerationRequestId: string | undefined = undefined - public testGenerationJob: TestGenerationJob | undefined - - // Start Test generation - public isSupportedLanguage: boolean = false - public conversationState: ConversationState = ConversationState.IDLE - public shortAnswer: ShortAnswer | undefined - public sourceFilePath: string = '' - public generatedFilePath: string = '' - public projectRootPath: string = '' - public fileLanguage: string | undefined = 'plaintext' - public stopIteration: boolean = false - public targetFileInfo: TargetFileInfo | undefined - public jobSummary: string = '' - - // Telemetry - public testGenerationStartTime: number = 0 - public hasUserPromptSupplied: boolean = false - public isCodeBlockSelected: boolean = false - public srcPayloadSize: number = 0 - public srcZipFileSize: number = 0 - public artifactsUploadDuration: number = 0 - public numberOfTestsGenerated: number = 0 - public linesOfCodeGenerated: number = 0 - public linesOfCodeAccepted: number = 0 - public charsOfCodeGenerated: number = 0 - public charsOfCodeAccepted: number = 0 - public latencyOfTestGeneration: number = 0 - - // TODO: Take values from ShortAnswer or TestGenerationJob - // Build loop - public buildStatus: BuildStatus = BuildStatus.SUCCESS - public updatedBuildCommands: string[] | undefined = undefined - public testCoveragePercentage: number = 90 - public isInProgress: boolean = false - public acceptedJobId = '' - public references: Reference[] = [] - - constructor() {} - - public isTabOpen(): boolean { - return this.tabID !== undefined - } -} diff --git a/packages/core/src/amazonqTest/chat/storages/chatSession.ts b/packages/core/src/amazonqTest/chat/storages/chatSession.ts deleted file mode 100644 index a8a3ccf429d..00000000000 --- a/packages/core/src/amazonqTest/chat/storages/chatSession.ts +++ /dev/null @@ -1,61 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - * - */ - -import { Session } from '../session/session' -import { getLogger } from '../../../shared/logger/logger' - -export class SessionNotFoundError extends Error {} - -export class ChatSessionManager { - private static _instance: ChatSessionManager - private activeSession: Session | undefined - private isInProgress: boolean = false - - constructor() {} - - public static get Instance() { - return this._instance || (this._instance = new this()) - } - - private createSession(): Session { - this.activeSession = new Session() - return this.activeSession - } - - public getSession(): Session { - if (this.activeSession === undefined) { - return this.createSession() - } - - return this.activeSession - } - - public getIsInProgress(): boolean { - return this.isInProgress - } - - public setIsInProgress(value: boolean): void { - this.isInProgress = value - } - - public setActiveTab(tabID: string): string { - getLogger().debug(`Setting active tab: ${tabID}, activeSession: ${this.activeSession}`) - if (this.activeSession !== undefined) { - this.activeSession.tabID = tabID - return tabID - } - - throw new SessionNotFoundError() - } - - public removeActiveTab(): void { - getLogger().debug(`Removing active tab and deleting activeSession: ${this.activeSession}`) - if (this.activeSession !== undefined) { - this.activeSession.tabID = undefined - this.activeSession = undefined - } - } -} diff --git a/packages/core/src/amazonqTest/chat/views/actions/uiMessageListener.ts b/packages/core/src/amazonqTest/chat/views/actions/uiMessageListener.ts deleted file mode 100644 index e44c002cdf9..00000000000 --- a/packages/core/src/amazonqTest/chat/views/actions/uiMessageListener.ts +++ /dev/null @@ -1,161 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { MessageListener } from '../../../../amazonq/messages/messageListener' -import { ExtensionMessage } from '../../../../amazonq/webview/ui/commands' -import { TestChatControllerEventEmitters } from '../../controller/controller' - -type UIMessage = ExtensionMessage & { - tabID?: string -} - -export interface UIMessageListenerProps { - readonly chatControllerEventEmitters: TestChatControllerEventEmitters - readonly webViewMessageListener: MessageListener -} - -export class UIMessageListener { - private testControllerEventsEmitters: TestChatControllerEventEmitters | undefined - private webViewMessageListener: MessageListener - - constructor(props: UIMessageListenerProps) { - this.testControllerEventsEmitters = props.chatControllerEventEmitters - this.webViewMessageListener = props.webViewMessageListener - - // Now we are listening to events that get sent from amazonq/webview/actions/actionListener (e.g. the tab) - this.webViewMessageListener.onMessage((msg) => { - this.handleMessage(msg) - }) - } - - private handleMessage(msg: ExtensionMessage) { - switch (msg.command) { - case 'new-tab-was-created': - this.tabOpened(msg) - break - case 'tab-was-removed': - this.tabClosed(msg) - break - case 'auth-follow-up-was-clicked': - this.authClicked(msg) - break - case 'start-test-gen': - this.startTestGen(msg) - break - case 'chat-prompt': - this.processChatPrompt(msg) - break - case 'form-action-click': - this.formActionClicked(msg) - break - case 'follow-up-was-clicked': - this.followUpClicked(msg) - break - case 'open-diff': - this.openDiff(msg) - break - case 'insert_code_at_cursor_position': - this.insertCodeAtCursorPosition(msg) - break - case 'response-body-link-click': - this.processResponseBodyLinkClick(msg) - break - case 'chat-item-voted': - this.chatItemVoted(msg) - break - case 'chat-item-feedback': - this.chatItemFeedback(msg) - break - } - } - - private tabOpened(msg: UIMessage) { - this.testControllerEventsEmitters?.tabOpened.fire({ - tabID: msg.tabID, - }) - } - - private tabClosed(msg: UIMessage) { - this.testControllerEventsEmitters?.tabClosed.fire({ - tabID: msg.tabID, - }) - } - - private authClicked(msg: UIMessage) { - this.testControllerEventsEmitters?.authClicked.fire({ - tabID: msg.tabID, - authType: msg.authType, - }) - } - - private startTestGen(msg: UIMessage) { - this.testControllerEventsEmitters?.startTestGen.fire({ - tabID: msg.tabID, - prompt: msg.prompt, - }) - } - - // Takes user input from chat input box. - private processChatPrompt(msg: UIMessage) { - this.testControllerEventsEmitters?.processHumanChatMessage.fire({ - prompt: msg.chatMessage, - tabID: msg.tabID, - }) - } - - private formActionClicked(msg: UIMessage) { - this.testControllerEventsEmitters?.formActionClicked.fire({ - ...msg, - }) - } - - private followUpClicked(msg: any) { - this.testControllerEventsEmitters?.followUpClicked.fire({ - followUp: msg.followUp, - tabID: msg.tabID, - }) - } - - private openDiff(msg: any) { - this.testControllerEventsEmitters?.openDiff.fire({ - tabID: msg.tabID, - filePath: msg.filePath, - deleted: msg.deleted, - messageId: msg.messageId, - }) - } - - private insertCodeAtCursorPosition(msg: any) { - this.testControllerEventsEmitters?.insertCodeAtCursorPosition.fire({ - command: msg.command, - messageId: msg.messageId, - tabID: msg.tabID, - code: msg.code, - insertionTargetType: msg.insertionTargetType, - codeReference: msg.codeReference, - }) - } - - private processResponseBodyLinkClick(msg: UIMessage) { - this.testControllerEventsEmitters?.processResponseBodyLinkClick.fire({ - command: msg.command, - messageId: msg.messageId, - tabID: msg.tabID, - link: msg.link, - }) - } - - private chatItemVoted(msg: any) { - this.testControllerEventsEmitters?.processChatItemVotedMessage.fire({ - tabID: msg.tabID, - command: msg.command, - vote: msg.vote, - }) - } - - private chatItemFeedback(msg: any) { - this.testControllerEventsEmitters?.processChatItemFeedbackMessage.fire(msg) - } -} diff --git a/packages/core/src/amazonqTest/chat/views/connector/connector.ts b/packages/core/src/amazonqTest/chat/views/connector/connector.ts deleted file mode 100644 index 86c7b446b97..00000000000 --- a/packages/core/src/amazonqTest/chat/views/connector/connector.ts +++ /dev/null @@ -1,256 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { AuthFollowUpType } from '../../../../amazonq/auth/model' -import { MessagePublisher } from '../../../../amazonq/messages/messagePublisher' -import { ChatItemAction, ChatItemButton, ProgressField, ChatItemContent } from '@aws/mynah-ui/dist/static' -import { ChatItemType } from '../../../../amazonq/commons/model' -import { testChat } from '../../../models/constants' -import { MynahIcons } from '@aws/mynah-ui' -import { SendBuildProgressMessageParams } from '../../controller/messenger/messenger' -import { CodeReference } from '../../../../codewhispererChat/view/connector/connector' - -class UiMessage { - readonly time: number = Date.now() - readonly sender: string = testChat - readonly type: TestMessageType = 'chatMessage' - readonly status: string = 'info' - - public constructor(protected tabID: string) {} -} - -export type TestMessageType = - | 'authenticationUpdateMessage' - | 'authNeededException' - | 'chatMessage' - | 'chatInputEnabledMessage' - | 'updatePlaceholderMessage' - | 'errorMessage' - | 'updatePromptProgress' - | 'chatSummaryMessage' - | 'buildProgressMessage' - -export class AuthenticationUpdateMessage { - readonly time: number = Date.now() - readonly sender: string = testChat - readonly type: TestMessageType = 'authenticationUpdateMessage' - - constructor( - readonly testEnabled: boolean, - readonly authenticatingTabIDs: string[] - ) {} -} - -export class UpdatePromptProgressMessage extends UiMessage { - readonly progressField: ProgressField | null - override type: TestMessageType = 'updatePromptProgress' - constructor(tabID: string, progressField: ProgressField | null) { - super(tabID) - this.progressField = progressField - } -} - -export class AuthNeededException extends UiMessage { - override type: TestMessageType = 'authNeededException' - - constructor( - readonly message: string, - readonly authType: AuthFollowUpType, - tabID: string - ) { - super(tabID) - } -} - -export interface ChatMessageProps { - readonly message: string | undefined - readonly messageId?: string | undefined - readonly messageType: ChatItemType - readonly buttons?: ChatItemButton[] - readonly followUps?: ChatItemAction[] - readonly canBeVoted?: boolean - readonly filePath?: string - readonly informationCard?: ChatItemContent['informationCard'] -} - -export class ChatMessage extends UiMessage { - readonly message: string | undefined - readonly messageId?: string | undefined - readonly messageType: ChatItemType - readonly canBeVoted?: boolean - readonly buttons?: ChatItemButton[] - readonly informationCard: ChatItemContent['informationCard'] - override type: TestMessageType = 'chatMessage' - - constructor(props: ChatMessageProps, tabID: string) { - super(tabID) - this.message = props.message - this.messageType = props.messageType - this.messageId = props.messageId || undefined - this.canBeVoted = props.canBeVoted || undefined - this.informationCard = props.informationCard || undefined - this.buttons = props.buttons || undefined - } -} - -export class ChatSummaryMessage extends UiMessage { - readonly message: string | undefined - readonly messageId?: string | undefined - readonly messageType: ChatItemType - readonly buttons: ChatItemButton[] - readonly canBeVoted?: boolean - readonly filePath?: string - override type: TestMessageType = 'chatSummaryMessage' - - constructor(props: ChatMessageProps, tabID: string) { - super(tabID) - this.message = props.message - this.messageType = props.messageType - this.buttons = props.buttons || [] - this.messageId = props.messageId || undefined - this.canBeVoted = props.canBeVoted - this.filePath = props.filePath - } -} - -export class ChatInputEnabledMessage extends UiMessage { - override type: TestMessageType = 'chatInputEnabledMessage' - - constructor( - tabID: string, - readonly enabled: boolean - ) { - super(tabID) - } -} - -export class UpdatePlaceholderMessage extends UiMessage { - readonly newPlaceholder: string - override type: TestMessageType = 'updatePlaceholderMessage' - - constructor(tabID: string, newPlaceholder: string) { - super(tabID) - this.newPlaceholder = newPlaceholder - } -} - -export class CapabilityCardMessage extends ChatMessage { - constructor(tabID: string) { - super( - { - message: '', - messageType: 'answer', - informationCard: { - title: '/test - Unit test generation', - description: 'Generate unit tests for selected code', - content: { - body: `I can generate unit tests for the active file or open project in your IDE. - -I can do things like: -- Add unit tests for highlighted functions -- Generate tests for null and empty inputs - -To learn more, visit the [Amazon Q Developer User Guide](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/test-generation.html).`, - }, - icon: 'check-list' as MynahIcons, - }, - }, - tabID - ) - } -} - -export class ErrorMessage extends UiMessage { - readonly title!: string - readonly message!: string - override type: TestMessageType = 'errorMessage' - - constructor(title: string, message: string, tabID: string) { - super(tabID) - this.title = title - this.message = message - } -} - -export class BuildProgressMessage extends UiMessage { - readonly message: string | undefined - readonly codeGenerationId!: string - readonly messageId?: string - readonly followUps?: { - text?: string - options?: ChatItemAction[] - } - readonly fileList?: { - fileTreeTitle?: string - rootFolderTitle?: string - filePaths?: string[] - } - readonly codeReference?: CodeReference[] - readonly canBeVoted: boolean - readonly messageType: ChatItemType - override type: TestMessageType = 'buildProgressMessage' - - constructor({ - tabID, - messageType, - codeGenerationId, - message, - canBeVoted, - messageId, - followUps, - fileList, - codeReference, - }: SendBuildProgressMessageParams) { - super(tabID) - this.messageType = messageType - this.codeGenerationId = codeGenerationId - this.message = message - this.canBeVoted = canBeVoted - this.messageId = messageId - this.followUps = followUps - this.fileList = fileList - this.codeReference = codeReference - } -} - -export class AppToWebViewMessageDispatcher { - constructor(private readonly appsToWebViewMessagePublisher: MessagePublisher) {} - - public sendChatMessage(message: ChatMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendChatSummaryMessage(message: ChatSummaryMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendUpdatePlaceholder(message: UpdatePlaceholderMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendAuthenticationUpdate(message: AuthenticationUpdateMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendAuthNeededExceptionMessage(message: AuthNeededException) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendChatInputEnabled(message: ChatInputEnabledMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendErrorMessage(message: ErrorMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendBuildProgressMessage(message: BuildProgressMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendUpdatePromptProgress(message: UpdatePromptProgressMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } -} diff --git a/packages/core/src/amazonqTest/error.ts b/packages/core/src/amazonqTest/error.ts deleted file mode 100644 index a6694b35863..00000000000 --- a/packages/core/src/amazonqTest/error.ts +++ /dev/null @@ -1,67 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -import { ToolkitError } from '../shared/errors' - -export const technicalErrorCustomerFacingMessage = - 'I am experiencing technical difficulties at the moment. Please try again in a few minutes.' -const defaultTestGenErrorMessage = 'Amazon Q encountered an error while generating tests. Try again later.' -export class TestGenError extends ToolkitError { - constructor( - error: string, - code: string, - public uiMessage: string - ) { - super(error, { code }) - } -} -export class ProjectZipError extends TestGenError { - constructor(error: string) { - super(error, 'ProjectZipError', defaultTestGenErrorMessage) - } -} -export class InvalidSourceZipError extends TestGenError { - constructor() { - super('Failed to create valid source zip', 'InvalidSourceZipError', defaultTestGenErrorMessage) - } -} -export class CreateUploadUrlError extends TestGenError { - constructor(errorMessage: string) { - super(errorMessage, 'CreateUploadUrlError', technicalErrorCustomerFacingMessage) - } -} -export class UploadTestArtifactToS3Error extends TestGenError { - constructor(error: string) { - super(error, 'UploadTestArtifactToS3Error', technicalErrorCustomerFacingMessage) - } -} -export class CreateTestJobError extends TestGenError { - constructor(error: string) { - super(error, 'CreateTestJobError', technicalErrorCustomerFacingMessage) - } -} -export class TestGenTimedOutError extends TestGenError { - constructor() { - super( - 'Test generation failed. Amazon Q timed out.', - 'TestGenTimedOutError', - technicalErrorCustomerFacingMessage - ) - } -} -export class TestGenStoppedError extends TestGenError { - constructor() { - super('Unit test generation cancelled.', 'TestGenCancelled', 'Unit test generation cancelled.') - } -} -export class TestGenFailedError extends TestGenError { - constructor(error?: string) { - super(error ?? 'Test generation failed', 'TestGenFailedError', error ?? technicalErrorCustomerFacingMessage) - } -} -export class ExportResultsArchiveError extends TestGenError { - constructor(error?: string) { - super(error ?? 'Test generation failed', 'ExportResultsArchiveError', technicalErrorCustomerFacingMessage) - } -} diff --git a/packages/core/src/amazonqTest/index.ts b/packages/core/src/amazonqTest/index.ts deleted file mode 100644 index 06f5ebb63f9..00000000000 --- a/packages/core/src/amazonqTest/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -export { default as MessengerUtils } from './chat/controller/messenger/messengerUtils' diff --git a/packages/core/src/amazonqTest/models/constants.ts b/packages/core/src/amazonqTest/models/constants.ts deleted file mode 100644 index 547cbdb3663..00000000000 --- a/packages/core/src/amazonqTest/models/constants.ts +++ /dev/null @@ -1,147 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -import { ProgressField, MynahIcons, ChatItemButton } from '@aws/mynah-ui' -import { ButtonActions } from '../chat/controller/messenger/messengerUtils' -import { TestGenerationBuildStep } from '../../codewhisperer/models/constants' -import { ChatSessionManager } from '../chat/storages/chatSession' -import { BuildStatus } from '../chat/session/session' - -// For uniquely identifiying which chat messages should be routed to Test -export const testChat = 'testChat' - -export const maxUserPromptLength = 4096 // user prompt character limit from MPS and API model. - -export const cancelTestGenButton: ChatItemButton = { - id: ButtonActions.STOP_TEST_GEN, - text: 'Cancel', - icon: 'cancel' as MynahIcons, -} - -export const testGenProgressField: ProgressField = { - status: 'default', - value: -1, - text: 'Generating unit tests...', - actions: [cancelTestGenButton], -} - -export const testGenCompletedField: ProgressField = { - status: 'success', - value: 100, - text: 'Complete...', - actions: [], -} - -export const cancellingProgressField: ProgressField = { - status: 'warning', - text: 'Cancelling...', - value: -1, - actions: [], -} - -export const cancelBuildProgressButton: ChatItemButton = { - id: ButtonActions.STOP_BUILD, - text: 'Cancel', - icon: 'cancel' as MynahIcons, -} - -export const buildProgressField: ProgressField = { - status: 'default', - value: -1, - text: 'Executing...', - actions: [cancelBuildProgressButton], -} - -export const errorProgressField: ProgressField = { - status: 'error', - text: 'Error...Input needed', - value: -1, - actions: [cancelBuildProgressButton], -} - -export const testGenSummaryMessage = ( - fileName: string, - planSummary?: string -) => `Sure. This may take a few minutes. I'll share updates here as I work on this. - -**Generating unit tests for the following methods in \`${fileName}\`** -${planSummary ? `\n\n${planSummary}` : ''} -` - -const checkIcons = { - wait: '☐', - current: '☐', - done: '', - error: '❌', -} - -interface StepStatus { - step: TestGenerationBuildStep - status: 'wait' | 'current' | 'done' | 'error' -} - -const stepStatuses: StepStatus[] = [] - -export const testGenBuildProgressMessage = (currentStep: TestGenerationBuildStep, status?: string) => { - const session = ChatSessionManager.Instance.getSession() - const statusText = BuildStatus[session.buildStatus].toLowerCase() - const icon = session.buildStatus === BuildStatus.SUCCESS ? checkIcons['done'] : checkIcons['error'] - let message = `Sure. This may take a few minutes and I'll share updates on my progress here. -**Progress summary**\n\n` - - if (currentStep === TestGenerationBuildStep.START_STEP) { - return message.trim() - } - - updateStepStatuses(currentStep, status) - - if (currentStep >= TestGenerationBuildStep.RUN_BUILD) { - message += `${getIconForStep(TestGenerationBuildStep.RUN_BUILD)} Started build execution\n` - } - - if (currentStep >= TestGenerationBuildStep.RUN_EXECUTION_TESTS) { - message += `${getIconForStep(TestGenerationBuildStep.RUN_EXECUTION_TESTS)} Executing tests\n` - } - - if (currentStep >= TestGenerationBuildStep.FIXING_TEST_CASES && session.buildStatus === BuildStatus.FAILURE) { - message += `${getIconForStep(TestGenerationBuildStep.FIXING_TEST_CASES)} Fixing errors in tests\n\n` - } - - if (currentStep > TestGenerationBuildStep.PROCESS_TEST_RESULTS) { - message += `**Test case summary** -${session.shortAnswer?.testCoverage ? `- Unit test coverage ${session.shortAnswer?.testCoverage}%` : ``} -${icon} Build ${statusText} -${icon} Assertion ${statusText}` - // TODO: Update Assertion % - } - - return message.trim() -} -// TODO: Work on UX to show the build error in the progress message -const updateStepStatuses = (currentStep: TestGenerationBuildStep, status?: string) => { - for (let step = TestGenerationBuildStep.INSTALL_DEPENDENCIES; step <= currentStep; step++) { - const stepStatus: StepStatus = { - step: step, - status: 'wait', - } - - if (step === currentStep) { - stepStatus.status = status === 'failed' ? 'error' : 'current' - } else if (step < currentStep) { - stepStatus.status = 'done' - } - - const existingIndex = stepStatuses.findIndex((s) => s.step === step) - if (existingIndex !== -1) { - stepStatuses[existingIndex] = stepStatus - } else { - stepStatuses.push(stepStatus) - } - } -} - -const getIconForStep = (step: TestGenerationBuildStep) => { - const stepStatus = stepStatuses.find((s) => s.step === step) - return stepStatus ? checkIcons[stepStatus.status] : checkIcons.wait -} diff --git a/packages/core/src/codewhisperer/commands/startTestGeneration.ts b/packages/core/src/codewhisperer/commands/startTestGeneration.ts deleted file mode 100644 index e99fd499e5a..00000000000 --- a/packages/core/src/codewhisperer/commands/startTestGeneration.ts +++ /dev/null @@ -1,259 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { getLogger } from '../../shared/logger/logger' -import { ZipUtil } from '../util/zipUtil' -import { ArtifactMap } from '../client/codewhisperer' -import { testGenerationLogsDir } from '../../shared/filesystemUtilities' -import { - createTestJob, - exportResultsArchive, - getPresignedUrlAndUploadTestGen, - pollTestJobStatus, - throwIfCancelled, -} from '../service/testGenHandler' -import path from 'path' -import { testGenState } from '../models/model' -import { ChatSessionManager } from '../../amazonqTest/chat/storages/chatSession' -import { ChildProcess, spawn } from 'child_process' // eslint-disable-line no-restricted-imports -import { BuildStatus } from '../../amazonqTest/chat/session/session' -import { fs } from '../../shared/fs/fs' -import { Range } from '../client/codewhispereruserclient' -import { AuthUtil } from '../indexNode' - -// eslint-disable-next-line unicorn/no-null -let spawnResult: ChildProcess | null = null -let isCancelled = false -export async function startTestGenerationProcess( - filePath: string, - userInputPrompt: string, - tabID: string, - initialExecution: boolean, - selectionRange?: Range -) { - const logger = getLogger() - const session = ChatSessionManager.Instance.getSession() - const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile - // TODO: Step 0: Initial Test Gen telemetry - try { - logger.verbose(`Starting Test Generation `) - logger.verbose(`Tab ID: ${tabID} !== ${session.tabID}`) - if (tabID !== session.tabID) { - logger.verbose(`Tab ID mismatch: ${tabID} !== ${session.tabID}`) - return - } - /** - * Step 1: Zip the project - */ - - const zipUtil = new ZipUtil() - if (initialExecution) { - const projectPath = zipUtil.getProjectPath(filePath) ?? '' - const relativeTargetPath = path.relative(projectPath, filePath) - session.listOfTestGenerationJobId = [] - session.shortAnswer = undefined - session.sourceFilePath = relativeTargetPath - session.projectRootPath = projectPath - session.listOfTestGenerationJobId = [] - } - const zipMetadata = await zipUtil.generateZipTestGen(session.projectRootPath, initialExecution) - session.srcPayloadSize = zipMetadata.buildPayloadSizeInBytes - session.srcZipFileSize = zipMetadata.zipFileSizeInBytes - - /** - * Step 2: Get presigned Url, upload and clean up - */ - throwIfCancelled() - if (!shouldContinueRunning(tabID)) { - return - } - let artifactMap: ArtifactMap = {} - const uploadStartTime = performance.now() - try { - artifactMap = await getPresignedUrlAndUploadTestGen(zipMetadata, profile) - } finally { - const outputLogPath = path.join(testGenerationLogsDir, 'output.log') - if (await fs.existsFile(outputLogPath)) { - await fs.delete(outputLogPath) - } - await zipUtil.removeTmpFiles(zipMetadata) - session.artifactsUploadDuration = performance.now() - uploadStartTime - } - - /** - * Step 3: Create scan job with startTestGeneration - */ - throwIfCancelled() - if (!shouldContinueRunning(tabID)) { - return - } - const sessionFilePath = session.sourceFilePath - const testJob = await createTestJob( - artifactMap, - [ - { - relativeTargetPath: sessionFilePath, - targetLineRangeList: selectionRange ? [selectionRange] : [], - }, - ], - userInputPrompt, - undefined, - profile - ) - if (!testJob.testGenerationJob) { - throw Error('Test job not found') - } - session.testGenerationJob = testJob.testGenerationJob - - /** - * Step 4: Polling mechanism on test job status with getTestGenStatus - */ - throwIfCancelled() - if (!shouldContinueRunning(tabID)) { - return - } - await pollTestJobStatus( - testJob.testGenerationJob.testGenerationJobId, - testJob.testGenerationJob.testGenerationJobGroupName, - filePath, - initialExecution, - profile - ) - // TODO: Send status to test summary - throwIfCancelled() - if (!shouldContinueRunning(tabID)) { - return - } - /** - * Step 5: Process and show the view diff by getting the results from exportResultsArchive - */ - // https://github.com/aws/aws-toolkit-vscode/blob/0164d4145e58ae036ddf3815455ea12a159d491d/packages/core/src/codewhisperer/service/transformByQ/transformationResultsViewProvider.ts#L314-L405 - await exportResultsArchive( - artifactMap.SourceCode, - testJob.testGenerationJob.testGenerationJobGroupName, - testJob.testGenerationJob.testGenerationJobId, - path.basename(session.projectRootPath), - session.projectRootPath, - initialExecution - ) - } catch (error) { - logger.error(`startTestGenerationProcess failed: %O`, error) - // TODO: Send error message to Chat - testGenState.getChatControllers()?.errorThrown.fire({ - tabID: session.tabID, - error: error, - }) - } finally { - testGenState.setToNotStarted() - } -} - -export function shouldContinueRunning(tabID: string): boolean { - if (tabID !== ChatSessionManager.Instance.getSession().tabID) { - getLogger().verbose(`Tab ID mismatch: ${tabID} !== ${ChatSessionManager.Instance.getSession().tabID}`) - return false - } - return true -} - -/** - * Run client side build with given build commands - */ -export async function runBuildCommand(listofBuildCommand: string[]): Promise { - for (const buildCommand of listofBuildCommand) { - try { - await fs.mkdir(testGenerationLogsDir) - const tmpFile = path.join(testGenerationLogsDir, 'output.log') - const result = await runLocalBuild(buildCommand, tmpFile) - if (result.isCancelled) { - return BuildStatus.CANCELLED - } - if (result.code !== 0) { - return BuildStatus.FAILURE - } - } catch (error) { - getLogger().error(`Build process error`) - return BuildStatus.FAILURE - } - } - return BuildStatus.SUCCESS -} - -function runLocalBuild( - buildCommand: string, - tmpFile: string -): Promise<{ code: number | null; isCancelled: boolean; message: string }> { - return new Promise(async (resolve, reject) => { - const environment = process.env - const repositoryPath = ChatSessionManager.Instance.getSession().projectRootPath - const [command, ...args] = buildCommand.split(' ') - getLogger().info(`Build process started for command: ${buildCommand}, for path: ${repositoryPath}`) - - let buildLogs = '' - - spawnResult = spawn(command, args, { - cwd: repositoryPath, - shell: true, - env: environment, - }) - - if (spawnResult.stdout) { - spawnResult.stdout.on('data', async (data) => { - const output = data.toString().trim() - getLogger().info(`BUILD OUTPUT: ${output}`) - buildLogs += output - }) - } - - if (spawnResult.stderr) { - spawnResult.stderr.on('data', async (data) => { - const output = data.toString().trim() - getLogger().warn(`BUILD ERROR: ${output}`) - buildLogs += output - }) - } - - spawnResult.on('close', async (code) => { - let message = '' - if (isCancelled) { - message = 'Build cancelled' - getLogger().info('BUILD CANCELLED') - } else if (code === 0) { - message = 'Build successful' - getLogger().info('BUILD SUCCESSFUL') - } else { - message = `Build failed with exit code ${code}` - getLogger().info(`BUILD FAILED with exit code ${code}`) - } - - try { - await fs.writeFile(tmpFile, buildLogs) - getLogger().info(`Build logs written to ${tmpFile}`) - } catch (error) { - getLogger().error(`Failed to write build logs to ${tmpFile}: ${error}`) - } - - resolve({ code, isCancelled, message }) - - // eslint-disable-next-line unicorn/no-null - spawnResult = null - isCancelled = false - }) - - spawnResult.on('error', (error) => { - reject(new Error(`Failed to start build process: ${error.message}`)) - }) - }) -} - -export function cancelBuild() { - if (spawnResult) { - isCancelled = true - spawnResult.kill() - getLogger().info('Build cancellation requested') - } else { - getLogger().info('No active build to cancel') - } -} diff --git a/packages/core/src/codewhisperer/models/model.ts b/packages/core/src/codewhisperer/models/model.ts index 2869098325e..70f520440fa 100644 --- a/packages/core/src/codewhisperer/models/model.ts +++ b/packages/core/src/codewhisperer/models/model.ts @@ -18,7 +18,6 @@ import globals from '../../shared/extensionGlobals' import { ChatControllerEventEmitters } from '../../amazonqGumby/chat/controller/controller' import { TransformationSteps } from '../client/codewhispereruserclient' import { Messenger } from '../../amazonqGumby/chat/controller/messenger/messenger' -import { TestChatControllerEventEmitters } from '../../amazonqTest/chat/controller/controller' import { ScanChatControllerEventEmitters } from '../../amazonqScan/controller' import { localize } from '../../shared/utilities/vsCodeUtils' @@ -372,55 +371,6 @@ export interface CodeLine { number: number } -/** - * Unit Test Generation - */ -enum TestGenStatus { - NotStarted, - Running, - Cancelling, -} -// TODO: Refactor model of /scan and /test -export class TestGenState { - // Define a constructor for this class - private testGenState: TestGenStatus = TestGenStatus.NotStarted - - protected chatControllers: TestChatControllerEventEmitters | undefined = undefined - - public isNotStarted() { - return this.testGenState === TestGenStatus.NotStarted - } - - public isRunning() { - return this.testGenState === TestGenStatus.Running - } - - public isCancelling() { - return this.testGenState === TestGenStatus.Cancelling - } - - public setToNotStarted() { - this.testGenState = TestGenStatus.NotStarted - } - - public setToCancelling() { - this.testGenState = TestGenStatus.Cancelling - } - - public setToRunning() { - this.testGenState = TestGenStatus.Running - } - - public setChatControllers(controllers: TestChatControllerEventEmitters) { - this.chatControllers = controllers - } - public getChatControllers() { - return this.chatControllers - } -} - -export const testGenState: TestGenState = new TestGenState() - enum CodeFixStatus { NotStarted, Running, diff --git a/packages/core/src/codewhisperer/service/securityScanHandler.ts b/packages/core/src/codewhisperer/service/securityScanHandler.ts index b83fdbebb1a..14485642aed 100644 --- a/packages/core/src/codewhisperer/service/securityScanHandler.ts +++ b/packages/core/src/codewhisperer/service/securityScanHandler.ts @@ -35,13 +35,11 @@ import { SecurityScanTimedOutError, UploadArtifactToS3Error, } from '../models/errors' -import { getTelemetryReasonDesc, isAwsError } from '../../shared/errors' +import { getTelemetryReasonDesc } from '../../shared/errors' import { CodeWhispererSettings } from '../util/codewhispererSettings' import { detectCommentAboveLine } from '../../shared/utilities/commentUtils' import { runtimeLanguageContext } from '../util/runtimeLanguageContext' import { FeatureUseCase } from '../models/constants' -import { UploadTestArtifactToS3Error } from '../../amazonqTest/error' -import { ChatSessionManager } from '../../amazonqTest/chat/storages/chatSession' import { AmazonqCreateUpload, Span, telemetry } from '../../shared/telemetry/telemetry' import { AuthUtil } from '../util/authUtil' @@ -432,10 +430,7 @@ export async function uploadArtifactToS3( } else { errorMessage = errorDesc ?? defaultMessage } - if (isAwsError(error) && featureUseCase === FeatureUseCase.TEST_GENERATION) { - ChatSessionManager.Instance.getSession().startTestGenerationRequestId = error.requestId - } - throw isCodeScan ? new UploadArtifactToS3Error(errorMessage) : new UploadTestArtifactToS3Error(errorMessage) + throw new UploadArtifactToS3Error(errorMessage) } finally { getLogger().debug(`Upload to S3 response details: x-amz-request-id: ${requestId}, x-amz-id-2: ${id2}`) if (span) { diff --git a/packages/core/src/codewhisperer/service/testGenHandler.ts b/packages/core/src/codewhisperer/service/testGenHandler.ts deleted file mode 100644 index 5ca8ca665da..00000000000 --- a/packages/core/src/codewhisperer/service/testGenHandler.ts +++ /dev/null @@ -1,326 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { ZipMetadata } from '../util/zipUtil' -import { getLogger } from '../../shared/logger/logger' -import * as CodeWhispererConstants from '../models/constants' -import * as codewhispererClient from '../client/codewhisperer' -import * as codeWhisperer from '../client/codewhisperer' -import CodeWhispererUserClient, { - ArtifactMap, - CreateUploadUrlRequest, - TargetCode, -} from '../client/codewhispereruserclient' -import { - CreateTestJobError, - CreateUploadUrlError, - ExportResultsArchiveError, - InvalidSourceZipError, - TestGenFailedError, - TestGenStoppedError, - TestGenTimedOutError, -} from '../../amazonqTest/error' -import { getMd5, uploadArtifactToS3 } from './securityScanHandler' -import { testGenState, Reference, RegionProfile } from '../models/model' -import { ChatSessionManager } from '../../amazonqTest/chat/storages/chatSession' -import { createCodeWhispererChatStreamingClient } from '../../shared/clients/codewhispererChatClient' -import { downloadExportResultArchive } from '../../shared/utilities/download' -import AdmZip from 'adm-zip' -import path from 'path' -import { ExportIntent } from '@amzn/codewhisperer-streaming' -import { glob } from 'glob' -import { UserWrittenCodeTracker } from '../tracker/userWrittenCodeTracker' -import { randomUUID } from '../../shared/crypto' -import { sleep } from '../../shared/utilities/timeoutUtils' -import { tempDirPath } from '../../shared/filesystemUtilities' -import fs from '../../shared/fs/fs' -import { AuthUtil } from '../util/authUtil' - -// TODO: Get TestFileName and Framework and to error message -export function throwIfCancelled() { - // TODO: fileName will be '' if user gives propt without opening - if (testGenState.isCancelling()) { - throw new TestGenStoppedError() - } -} - -export async function getPresignedUrlAndUploadTestGen(zipMetadata: ZipMetadata, profile: RegionProfile | undefined) { - const logger = getLogger() - if (zipMetadata.zipFilePath === '') { - getLogger().error('Failed to create valid source zip') - throw new InvalidSourceZipError() - } - const srcReq: CreateUploadUrlRequest = { - contentMd5: getMd5(zipMetadata.zipFilePath), - artifactType: 'SourceCode', - uploadIntent: CodeWhispererConstants.testGenUploadIntent, - profileArn: profile?.arn, - } - logger.verbose(`Prepare for uploading src context...`) - const srcResp = await codeWhisperer.codeWhispererClient.createUploadUrl(srcReq).catch((err) => { - getLogger().error(`Failed getting presigned url for uploading src context. Request id: ${err.requestId}`) - throw new CreateUploadUrlError(err.message) - }) - logger.verbose(`CreateUploadUrlRequest requestId: ${srcResp.$response.requestId}`) - logger.verbose(`Complete Getting presigned Url for uploading src context.`) - logger.verbose(`Uploading src context...`) - await uploadArtifactToS3(zipMetadata.zipFilePath, srcResp, CodeWhispererConstants.FeatureUseCase.TEST_GENERATION) - logger.verbose(`Complete uploading src context.`) - const artifactMap: ArtifactMap = { - SourceCode: srcResp.uploadId, - } - return artifactMap -} - -export async function createTestJob( - artifactMap: codewhispererClient.ArtifactMap, - relativeTargetPath: TargetCode[], - userInputPrompt: string, - clientToken?: string, - profile?: RegionProfile -) { - const logger = getLogger() - logger.verbose(`Creating test job and starting startTestGeneration...`) - - // JS will minify this input object - fix that - const targetCodeList = relativeTargetPath.map((targetCode) => ({ - relativeTargetPath: targetCode.relativeTargetPath, - targetLineRangeList: targetCode.targetLineRangeList?.map((range) => ({ - start: { line: range.start.line, character: range.start.character }, - end: { line: range.end.line, character: range.end.character }, - })), - })) - logger.debug('updated target code list: %O', targetCodeList) - const req: CodeWhispererUserClient.StartTestGenerationRequest = { - uploadId: artifactMap.SourceCode, - targetCodeList, - userInput: userInputPrompt, - testGenerationJobGroupName: ChatSessionManager.Instance.getSession().testGenerationJobGroupName ?? randomUUID(), // TODO: remove fallback - clientToken, - profileArn: profile?.arn, - } - logger.debug('Unit test generation request body: %O', req) - logger.debug('target code list: %O', req.targetCodeList[0]) - const firstTargetCodeList = req.targetCodeList?.[0] - const firstTargetLineRangeList = firstTargetCodeList?.targetLineRangeList?.[0] - logger.debug('target line range list: %O', firstTargetLineRangeList) - logger.debug('target line range start: %O', firstTargetLineRangeList?.start) - logger.debug('target line range end: %O', firstTargetLineRangeList?.end) - - const resp = await codewhispererClient.codeWhispererClient.startTestGeneration(req).catch((err) => { - ChatSessionManager.Instance.getSession().startTestGenerationRequestId = err.requestId - logger.error(`Failed creating test job. Request id: ${err.requestId}`) - throw new CreateTestJobError(err.message) - }) - logger.info('Unit test generation request id: %s', resp.$response.requestId) - logger.debug('Unit test generation data: %O', resp.$response.data) - ChatSessionManager.Instance.getSession().startTestGenerationRequestId = resp.$response.requestId - if (resp.$response.error) { - logger.error('Unit test generation error: %O', resp.$response.error) - } - if (resp.testGenerationJob) { - ChatSessionManager.Instance.getSession().listOfTestGenerationJobId.push( - resp.testGenerationJob?.testGenerationJobId - ) - ChatSessionManager.Instance.getSession().testGenerationJobGroupName = - resp.testGenerationJob?.testGenerationJobGroupName - } - return resp -} - -export async function pollTestJobStatus( - jobId: string, - jobGroupName: string, - filePath: string, - initialExecution: boolean, - profile?: RegionProfile -) { - const session = ChatSessionManager.Instance.getSession() - const pollingStartTime = performance.now() - // We don't expect to get results immediately, so sleep for some time initially to not make unnecessary calls - await sleep(CodeWhispererConstants.testGenPollingDelaySeconds) - - const logger = getLogger() - logger.verbose(`Polling testgen job status...`) - let status = CodeWhispererConstants.TestGenerationJobStatus.IN_PROGRESS - while (true) { - throwIfCancelled() - const req: CodeWhispererUserClient.GetTestGenerationRequest = { - testGenerationJobId: jobId, - testGenerationJobGroupName: jobGroupName, - profileArn: profile?.arn, - } - const resp = await codewhispererClient.codeWhispererClient.getTestGeneration(req) - logger.verbose('pollTestJobStatus request id: %s', resp.$response.requestId) - logger.debug('pollTestJobStatus testGenerationJob %O', resp.testGenerationJob) - ChatSessionManager.Instance.getSession().testGenerationJob = resp.testGenerationJob - const progressRate = resp.testGenerationJob?.progressRate ?? 0 - testGenState.getChatControllers()?.sendUpdatePromptProgress.fire({ - tabID: ChatSessionManager.Instance.getSession().tabID, - status: 'InProgress', - progressRate, - }) - const jobSummary = resp.testGenerationJob?.jobSummary ?? '' - const jobSummaryNoBackticks = jobSummary.replace(/^`+|`+$/g, '') - ChatSessionManager.Instance.getSession().jobSummary = jobSummaryNoBackticks - const packageInfoList = resp.testGenerationJob?.packageInfoList ?? [] - const packageInfo = packageInfoList[0] - const targetFileInfo = packageInfo?.targetFileInfoList?.[0] - - if (packageInfo) { - // TODO: will need some fields from packageInfo such as buildCommand, packagePlan, packageSummary - } - if (targetFileInfo) { - if (targetFileInfo.numberOfTestMethods) { - session.numberOfTestsGenerated = Number(targetFileInfo.numberOfTestMethods) - } - if (targetFileInfo.codeReferences) { - session.references = targetFileInfo.codeReferences as Reference[] - } - if (initialExecution) { - session.generatedFilePath = targetFileInfo.testFilePath ?? '' - const currentPlanSummary = session.targetFileInfo?.filePlan - const newPlanSummary = targetFileInfo?.filePlan - - if (currentPlanSummary !== newPlanSummary && newPlanSummary) { - const chatControllers = testGenState.getChatControllers() - if (chatControllers) { - const currentSession = ChatSessionManager.Instance.getSession() - chatControllers.updateTargetFileInfo.fire({ - tabID: currentSession.tabID, - targetFileInfo, - testGenerationJobGroupName: resp.testGenerationJob?.testGenerationJobGroupName, - testGenerationJobId: resp.testGenerationJob?.testGenerationJobId, - filePath, - }) - } - } - } - } - ChatSessionManager.Instance.getSession().targetFileInfo = targetFileInfo - status = resp.testGenerationJob?.status as CodeWhispererConstants.TestGenerationJobStatus - if (status === CodeWhispererConstants.TestGenerationJobStatus.FAILED) { - session.numberOfTestsGenerated = 0 - logger.verbose(`Test generation failed.`) - if (resp.testGenerationJob?.jobStatusReason) { - session.stopIteration = true - throw new TestGenFailedError(resp.testGenerationJob?.jobStatusReason) - } else { - throw new TestGenFailedError() - } - } else if (status === CodeWhispererConstants.TestGenerationJobStatus.COMPLETED) { - logger.verbose(`testgen job status: ${status}`) - logger.verbose(`Complete polling test job status.`) - break - } - throwIfCancelled() - await sleep(CodeWhispererConstants.testGenJobPollingIntervalMilliseconds) - const elapsedTime = performance.now() - pollingStartTime - if (elapsedTime > CodeWhispererConstants.testGenJobTimeoutMilliseconds) { - logger.verbose(`testgen job status: ${status}`) - logger.verbose(`testgen job failed. Amazon Q timed out.`) - throw new TestGenTimedOutError() - } - } - return status -} - -/** - * Download the zip from exportResultsArchieve API and store in temp zip - */ -export async function exportResultsArchive( - uploadId: string, - groupName: string, - jobId: string, - projectName: string, - projectPath: string, - initialExecution: boolean -) { - // TODO: Make a common Temp folder - const pathToArchiveDir = path.join(tempDirPath, 'q-testgen') - - const archivePathExists = await fs.existsDir(pathToArchiveDir) - if (archivePathExists) { - await fs.delete(pathToArchiveDir, { recursive: true }) - } - await fs.mkdir(pathToArchiveDir) - - let downloadErrorMessage = undefined - - const session = ChatSessionManager.Instance.getSession() - try { - const pathToArchive = path.join(pathToArchiveDir, 'QTestGeneration.zip') - // Download and deserialize the zip - await downloadResultArchive(uploadId, groupName, jobId, pathToArchive) - const zip = new AdmZip(pathToArchive) - zip.extractAllTo(pathToArchiveDir, true) - - const testFilePathFromResponse = session?.targetFileInfo?.testFilePath - const testFilePath = testFilePathFromResponse - ? testFilePathFromResponse.split('/').slice(1).join('/') // remove the project name - : await getTestFilePathFromZip(pathToArchiveDir) - if (initialExecution) { - testGenState.getChatControllers()?.showCodeGenerationResults.fire({ - tabID: session.tabID, - filePath: testFilePath, - projectName, - }) - - // If User accepts the diff - testGenState.getChatControllers()?.sendUpdatePromptProgress.fire({ - tabID: ChatSessionManager.Instance.getSession().tabID, - status: 'Completed', - }) - } - } catch (e) { - session.numberOfTestsGenerated = 0 - downloadErrorMessage = (e as Error).message - getLogger().error(`Unit Test Generation: ExportResultArchive error = ${downloadErrorMessage}`) - throw new ExportResultsArchiveError(downloadErrorMessage) - } -} - -async function getTestFilePathFromZip(pathToArchiveDir: string) { - const resultArtifactsDir = path.join(pathToArchiveDir, 'resultArtifacts') - const paths = await glob([resultArtifactsDir + '/**/*', '!**/.DS_Store'], { nodir: true }) - const absolutePath = paths[0] - const result = path.relative(resultArtifactsDir, absolutePath) - return result -} - -export async function downloadResultArchive( - uploadId: string, - testGenerationJobGroupName: string, - testGenerationJobId: string, - pathToArchive: string -) { - let downloadErrorMessage = undefined - const cwStreamingClient = await createCodeWhispererChatStreamingClient() - - try { - await downloadExportResultArchive( - cwStreamingClient, - { - exportId: uploadId, - exportIntent: ExportIntent.UNIT_TESTS, - exportContext: { - unitTestGenerationExportContext: { - testGenerationJobGroupName, - testGenerationJobId, - }, - }, - }, - pathToArchive, - AuthUtil.instance.regionProfileManager.activeRegionProfile - ) - } catch (e: any) { - downloadErrorMessage = (e as Error).message - getLogger().error(`Unit Test Generation: ExportResultArchive error = ${downloadErrorMessage}`) - throw new ExportResultsArchiveError(downloadErrorMessage) - } finally { - cwStreamingClient.destroy() - UserWrittenCodeTracker.instance.onQFeatureInvoked() - } -} diff --git a/packages/core/src/codewhisperer/util/telemetryHelper.ts b/packages/core/src/codewhisperer/util/telemetryHelper.ts index 060a5ecb282..89c04afe572 100644 --- a/packages/core/src/codewhisperer/util/telemetryHelper.ts +++ b/packages/core/src/codewhisperer/util/telemetryHelper.ts @@ -13,7 +13,6 @@ import { CodewhispererPreviousSuggestionState, CodewhispererUserDecision, CodewhispererUserTriggerDecision, - Status, telemetry, } from '../../shared/telemetry/telemetry' import { CodewhispererCompletionType, CodewhispererSuggestionState } from '../../shared/telemetry/telemetry' @@ -28,7 +27,6 @@ import { CodeWhispererSupplementalContext } from '../models/model' import { FeatureConfigProvider } from '../../shared/featureConfig' import CodeWhispererUserClient, { CodeScanRemediationsEventType } from '../client/codewhispereruserclient' import { CodeAnalysisScope as CodeAnalysisScopeClientSide } from '../models/constants' -import { Session } from '../../amazonqTest/chat/session/session' import { sleep } from '../../shared/utilities/timeoutUtils' import { getDiagnosticsDifferences, getDiagnosticsOfCurrentFile, toIdeDiagnostics } from './diagnosticsUtil' import { Auth } from '../../auth/auth' @@ -71,54 +69,6 @@ export class TelemetryHelper { return (this.#instance ??= new this()) } - public sendTestGenerationToolkitEvent( - session: Session, - isSupportedLanguage: boolean, - isFileInWorkspace: boolean, - result: 'Succeeded' | 'Failed' | 'Cancelled', - requestId?: string, - perfClientLatency?: number, - reasonDesc?: string, - isCodeBlockSelected?: boolean, - artifactsUploadDuration?: number, - buildPayloadBytes?: number, - buildZipFileBytes?: number, - acceptedCharactersCount?: number, - acceptedCount?: number, - acceptedLinesCount?: number, - generatedCharactersCount?: number, - generatedCount?: number, - generatedLinesCount?: number, - reason?: string, - status?: Status - ) { - telemetry.amazonq_utgGenerateTests.emit({ - cwsprChatProgrammingLanguage: session.fileLanguage ?? 'plaintext', - hasUserPromptSupplied: session.hasUserPromptSupplied, - isSupportedLanguage: session.isSupportedLanguage, - isFileInWorkspace: isFileInWorkspace, - result: result, - artifactsUploadDuration: artifactsUploadDuration, - buildPayloadBytes: buildPayloadBytes, - buildZipFileBytes: buildZipFileBytes, - credentialStartUrl: AuthUtil.instance.startUrl, - acceptedCharactersCount: acceptedCharactersCount, - acceptedCount: acceptedCount, - acceptedLinesCount: acceptedLinesCount, - generatedCharactersCount: generatedCharactersCount, - generatedCount: generatedCount, - generatedLinesCount: generatedLinesCount, - isCodeBlockSelected: isCodeBlockSelected, - jobGroup: session.testGenerationJobGroupName, - jobId: session.listOfTestGenerationJobId[0], - perfClientLatency: perfClientLatency, - requestId: requestId, - reasonDesc: reasonDesc, - reason: reason, - status: status, - }) - } - public recordServiceInvocationTelemetry( requestId: string, sessionId: string, diff --git a/packages/core/src/codewhisperer/util/zipUtil.ts b/packages/core/src/codewhisperer/util/zipUtil.ts index 32687a6452c..719116efdc7 100644 --- a/packages/core/src/codewhisperer/util/zipUtil.ts +++ b/packages/core/src/codewhisperer/util/zipUtil.ts @@ -4,7 +4,7 @@ */ import * as vscode from 'vscode' import path from 'path' -import { tempDirPath, testGenerationLogsDir } from '../../shared/filesystemUtilities' +import { tempDirPath } from '../../shared/filesystemUtilities' import { getLogger } from '../../shared/logger/logger' import * as CodeWhispererConstants from '../models/constants' import { ToolkitError } from '../../shared/errors' @@ -21,7 +21,6 @@ import { } from '../models/errors' import { FeatureUseCase } from '../models/constants' import { ChildProcess, ChildProcessOptions } from '../../shared/utilities/processUtils' -import { ProjectZipError } from '../../amazonqTest/error' import { removeAnsi } from '../../shared/utilities/textUtilities' import { normalize } from '../../shared/utilities/pathUtils' import { ZipStream } from '../../shared/utilities/zipStream' @@ -570,56 +569,6 @@ export class ZipUtil { } } - public async generateZipTestGen(projectPath: string, initialExecution: boolean): Promise { - try { - // const repoMapFile = await LspClient.instance.getRepoMapJSON() - const zipDirPath = this.getZipDirPath(FeatureUseCase.TEST_GENERATION) - - const metadataDir = path.join(zipDirPath, 'utgRequiredArtifactsDir') - - // Create directories - const dirs = { - metadata: metadataDir, - buildAndExecuteLogDir: path.join(metadataDir, 'buildAndExecuteLogDir'), - repoMapDir: path.join(metadataDir, 'repoMapData'), - testCoverageDir: path.join(metadataDir, 'testCoverageDir'), - } - await Promise.all(Object.values(dirs).map((dir) => fs.mkdir(dir))) - - // if (await fs.exists(repoMapFile)) { - // await fs.copy(repoMapFile, path.join(dirs.repoMapDir, 'repoMapData.json')) - // await fs.delete(repoMapFile) - // } - - if (!initialExecution) { - await this.processTestCoverageFiles(dirs.testCoverageDir) - - const sourcePath = path.join(testGenerationLogsDir, 'output.log') - const targetPath = path.join(dirs.buildAndExecuteLogDir, 'output.log') - if (await fs.exists(sourcePath)) { - await fs.copy(sourcePath, targetPath) - } - } - - const zipFilePath: string = await this.zipProject(FeatureUseCase.TEST_GENERATION, projectPath, metadataDir) - const zipFileSize = (await fs.stat(zipFilePath)).size - return { - rootDir: zipDirPath, - zipFilePath: zipFilePath, - srcPayloadSizeInBytes: this._totalSize, - scannedFiles: new Set(this._pickedSourceFiles), - zipFileSizeInBytes: zipFileSize, - buildPayloadSizeInBytes: this._totalBuildSize, - lines: this._totalLines, - language: this._language, - } - } catch (error) { - getLogger().error('Zip error caused by: %s', error) - throw new ProjectZipError( - error instanceof Error ? error.message : 'Unknown error occurred during zip operation' - ) - } - } // TODO: Refactor this public async removeTmpFiles(zipMetadata: ZipMetadata, scope?: CodeWhispererConstants.CodeAnalysisScope) { const logger = getLoggerForScope(scope) diff --git a/packages/core/src/shared/db/chatDb/util.ts b/packages/core/src/shared/db/chatDb/util.ts index fc681b2b5a5..316cbe5660c 100644 --- a/packages/core/src/shared/db/chatDb/util.ts +++ b/packages/core/src/shared/db/chatDb/util.ts @@ -267,16 +267,10 @@ function getTabTypeIcon(tabType: TabType): MynahIconsType { switch (tabType) { case 'cwc': return 'chat' - case 'doc': - return 'file' case 'review': return 'bug' case 'gumby': return 'transform' - case 'testgen': - return 'check-list' - case 'featuredev': - return 'code-block' default: return 'chat' } diff --git a/packages/core/src/shared/filesystemUtilities.ts b/packages/core/src/shared/filesystemUtilities.ts index 54ca5b4b0e1..6414fd11b66 100644 --- a/packages/core/src/shared/filesystemUtilities.ts +++ b/packages/core/src/shared/filesystemUtilities.ts @@ -20,8 +20,6 @@ export const tempDirPath = path.join( 'aws-toolkit-vscode' ) -export const testGenerationLogsDir = path.join(tempDirPath, 'testGenerationLogs') - export async function getDirSize(dirPath: string, startTime: number, duration: number): Promise { if (performance.now() - startTime > duration) { getLogger().warn('getDirSize: exceeds time limit') diff --git a/packages/core/src/test/codewhisperer/zipUtil.test.ts b/packages/core/src/test/codewhisperer/zipUtil.test.ts index e6c4f4148e5..102bf2fc441 100644 --- a/packages/core/src/test/codewhisperer/zipUtil.test.ts +++ b/packages/core/src/test/codewhisperer/zipUtil.test.ts @@ -7,15 +7,12 @@ import assert from 'assert' import vscode from 'vscode' import sinon from 'sinon' import { join } from 'path' -import path from 'path' import JSZip from 'jszip' import { getTestWorkspaceFolder } from '../../testInteg/integrationTestsUtilities' import { ZipUtil } from '../../codewhisperer/util/zipUtil' import { CodeAnalysisScope, codeScanTruncDirPrefix } from '../../codewhisperer/models/constants' import { ToolkitError } from '../../shared/errors' import { fs } from '../../shared/fs/fs' -import { tempDirPath } from '../../shared/filesystemUtilities' -import { CodeWhispererConstants } from '../../codewhisperer/indexNode' describe('zipUtil', function () { const workspaceFolder = getTestWorkspaceFolder() @@ -140,43 +137,4 @@ describe('zipUtil', function () { assert.ok(files.includes(join('workspaceFolder', 'workspaceFolder', 'App.java'))) }) }) - - describe('generateZipTestGen', function () { - let zipUtil: ZipUtil - let getZipDirPathStub: sinon.SinonStub - let testTempDirPath: string - - beforeEach(function () { - zipUtil = new ZipUtil() - testTempDirPath = path.join(tempDirPath, CodeWhispererConstants.TestGenerationTruncDirPrefix) - getZipDirPathStub = sinon.stub(zipUtil, 'getZipDirPath') - getZipDirPathStub.callsFake(() => testTempDirPath) - }) - - afterEach(function () { - sinon.restore() - }) - - it('should generate zip for test generation successfully', async function () { - const mkdirSpy = sinon.spy(fs, 'mkdir') - - const result = await zipUtil.generateZipTestGen(appRoot, false) - - assert.ok(mkdirSpy.calledWith(path.join(testTempDirPath, 'utgRequiredArtifactsDir'))) - assert.ok( - mkdirSpy.calledWith(path.join(testTempDirPath, 'utgRequiredArtifactsDir', 'buildAndExecuteLogDir')) - ) - assert.ok(mkdirSpy.calledWith(path.join(testTempDirPath, 'utgRequiredArtifactsDir', 'repoMapData'))) - assert.ok(mkdirSpy.calledWith(path.join(testTempDirPath, 'utgRequiredArtifactsDir', 'testCoverageDir'))) - - assert.strictEqual(result.rootDir, testTempDirPath) - assert.strictEqual(result.zipFilePath, testTempDirPath + CodeWhispererConstants.codeScanZipExt) - assert.ok(result.srcPayloadSizeInBytes > 0) - assert.strictEqual(result.buildPayloadSizeInBytes, 0) - assert.ok(result.zipFileSizeInBytes > 0) - assert.strictEqual(result.lines, 150) - assert.strictEqual(result.language, 'java') - assert.strictEqual(result.scannedFiles.size, 4) - }) - }) }) From 6c79154cbdedbd5cbdbfe0c049063008246ce0e2 Mon Sep 17 00:00:00 2001 From: abhraina-aws Date: Thu, 24 Jul 2025 11:29:18 -0700 Subject: [PATCH 57/69] fix(amazonq): disable SageMakerUnifiedStudio for show logs (#7747) ## Problem Added another code setting for show logs. ## Solution Added another code setting for show logs. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/amazonq/package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index 86b2f45f41b..39f4f270406 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -410,7 +410,7 @@ }, { "command": "aws.amazonq.showLogs", - "when": "view == aws.amazonq.AmazonQChatView", + "when": "!aws.isSageMakerUnifiedStudio", "group": "1_amazonQ@5" }, { @@ -644,8 +644,7 @@ { "command": "aws.amazonq.showLogs", "title": "%AWS.command.codewhisperer.showLogs%", - "category": "%AWS.amazonq.title%", - "enablement": "aws.codewhisperer.connected" + "category": "%AWS.amazonq.title%" }, { "command": "aws.amazonq.selectRegionProfile", From 7f36a2d6064cf934b7dda1344b9c43cac710e4e0 Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:43:54 -0700 Subject: [PATCH 58/69] config(amazonq): disable inline tutorial since it's taking ~250ms for all users no matter it's shown or not (#7722) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … ## Problem by design, the code path should only be applied to "new" users, however currently it's taking 250ms for all users no matter the UI is displayed or not. image ## Solution --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/amazonq/src/app/inline/completion.ts | 30 +++++------ .../src/app/inline/recommendationService.ts | 5 +- .../tutorials/inlineTutorialAnnotation.ts | 51 ++++++++----------- 3 files changed, 40 insertions(+), 46 deletions(-) diff --git a/packages/amazonq/src/app/inline/completion.ts b/packages/amazonq/src/app/inline/completion.ts index be49a0654cc..9aaf40c4c43 100644 --- a/packages/amazonq/src/app/inline/completion.ts +++ b/packages/amazonq/src/app/inline/completion.ts @@ -198,7 +198,7 @@ export class InlineCompletionManager implements Disposable { } export class AmazonQInlineCompletionItemProvider implements InlineCompletionItemProvider { - private logger = getLogger('nextEditPrediction') + private logger = getLogger() constructor( private readonly languageClient: LanguageClient, private readonly recommendationService: RecommendationService, @@ -299,7 +299,8 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem } // re-use previous suggestions as long as new typed prefix matches if (prevItemMatchingPrefix.length > 0) { - getLogger().debug(`Re-using suggestions that match user typed characters`) + logstr += `- not call LSP and reuse previous suggestions that match user typed characters + - duration between trigger to completion suggestion is displayed ${performance.now() - t0}` return prevItemMatchingPrefix } getLogger().debug(`Auto rejecting suggestions from previous session`) @@ -318,7 +319,6 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem this.sessionManager.clear() } - // TODO: this line will take ~200ms each trigger, need to root cause and maybe better to disable it for now // tell the tutorial that completions has been triggered await this.inlineTutorialAnnotation.triggered(context.triggerKind) @@ -346,12 +346,13 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem const t2 = performance.now() - logstr = logstr += `- number of suggestions: ${items.length} + logstr += `- number of suggestions: ${items.length} - sessionId: ${this.sessionManager.getActiveSession()?.sessionId} - first suggestion content (next line): ${itemLog} -- duration since trigger to before sending Flare call: ${t1 - t0}ms -- duration since trigger to receiving responses from Flare: ${t2 - t0}ms +- duration between trigger to before sending LSP call: ${t1 - t0}ms +- duration between trigger to after receiving LSP response: ${t2 - t0}ms +- duration between before sending LSP call to after receving LSP response: ${t2 - t1}ms ` const session = this.sessionManager.getActiveSession() @@ -361,16 +362,13 @@ ${itemLog} } if (!session || !items.length || !editor) { - getLogger().debug( - `Failed to produce inline suggestion results. Received ${items.length} items from service` - ) + logstr += `Failed to produce inline suggestion results. Received ${items.length} items from service` return [] } const cursorPosition = document.validatePosition(position) if (position.isAfter(editor.selection.active)) { - getLogger().debug(`Cursor moved behind trigger position. Discarding suggestion...`) const params: LogInlineCompletionSessionResultsParams = { sessionId: session.sessionId, completionSessionResult: { @@ -383,6 +381,7 @@ ${itemLog} } this.languageClient.sendNotification(this.logSessionResultMessageName, params) this.sessionManager.clear() + logstr += `- cursor moved behind trigger position. Discarding suggestion...` return [] } @@ -410,9 +409,7 @@ ${itemLog} // Check if Next Edit Prediction feature flag is enabled if (Experiments.instance.get('amazonqLSPNEP', true)) { await showEdits(item, editor, session, this.languageClient, this) - const t3 = performance.now() - logstr = logstr + `- duration since trigger to NEP suggestion is displayed: ${t3 - t0}ms` - this.logger.info(logstr) + logstr += `- duration between trigger to edits suggestion is displayed: ${performance.now() - t0}ms` } return [] } @@ -438,9 +435,6 @@ ${itemLog} // report discard if none of suggestions match typeahead if (itemsMatchingTypeahead.length === 0) { - getLogger().debug( - `Suggestion does not match user typeahead from insertion position. Discarding suggestion...` - ) const params: LogInlineCompletionSessionResultsParams = { sessionId: session.sessionId, completionSessionResult: { @@ -453,17 +447,21 @@ ${itemLog} } this.languageClient.sendNotification(this.logSessionResultMessageName, params) this.sessionManager.clear() + logstr += `- suggestion does not match user typeahead from insertion position. Discarding suggestion...` return [] } this.sessionManager.updateCodeReferenceAndImports() // suggestions returned here will be displayed on screen + logstr += `- duration between trigger to completion suggestion is displayed: ${performance.now() - t0}ms` return itemsMatchingTypeahead as InlineCompletionItem[] } catch (e) { getLogger('amazonqLsp').error('Failed to provide completion items: %O', e) + logstr += `- failed to provide completion items ${(e as Error).message}` return [] } finally { vsCodeState.isRecommendationsActive = false + this.logger.info(logstr) } } } diff --git a/packages/amazonq/src/app/inline/recommendationService.ts b/packages/amazonq/src/app/inline/recommendationService.ts index 1329c68a51c..c7ba5c3b4b7 100644 --- a/packages/amazonq/src/app/inline/recommendationService.ts +++ b/packages/amazonq/src/app/inline/recommendationService.ts @@ -92,13 +92,15 @@ export class RecommendationService { nextToken: request.partialResultToken, }, }) + const t0 = performance.now() const result: InlineCompletionListWithReferences = await languageClient.sendRequest( inlineCompletionWithReferencesRequestType.method, request, token ) - getLogger().info('Received inline completion response: %O', { + getLogger().info('Received inline completion response from LSP: %O', { sessionId: result.sessionId, + latency: performance.now() - t0, itemCount: result.items?.length || 0, items: result.items?.map((item) => ({ itemId: item.itemId, @@ -128,6 +130,7 @@ export class RecommendationService { const isInlineEdit = result.items.some((item) => item.isInlineEdit) + // TODO: question, is it possible that the first request returns empty suggestion but has non-empty next token? if (result.partialResultToken) { if (!isInlineEdit) { // If the suggestion is COMPLETIONS and there are more results to fetch, handle them in the background diff --git a/packages/amazonq/src/app/inline/tutorials/inlineTutorialAnnotation.ts b/packages/amazonq/src/app/inline/tutorials/inlineTutorialAnnotation.ts index bd12b1d28dd..ad0807df94c 100644 --- a/packages/amazonq/src/app/inline/tutorials/inlineTutorialAnnotation.ts +++ b/packages/amazonq/src/app/inline/tutorials/inlineTutorialAnnotation.ts @@ -5,13 +5,7 @@ import * as vscode from 'vscode' import * as os from 'os' -import { - AnnotationChangeSource, - AuthUtil, - inlinehintKey, - runtimeLanguageContext, - TelemetryHelper, -} from 'aws-core-vscode/codewhisperer' +import { AnnotationChangeSource, AuthUtil, inlinehintKey, runtimeLanguageContext } from 'aws-core-vscode/codewhisperer' import { editorUtilities, getLogger, globals, setContext, vscodeUtilities } from 'aws-core-vscode/shared' import { LinesChangeEvent, LineSelection, LineTracker } from '../stateTracker/lineTracker' import { telemetry } from 'aws-core-vscode/telemetry' @@ -296,28 +290,27 @@ export class InlineTutorialAnnotation implements vscode.Disposable { } async triggered(triggerType: vscode.InlineCompletionTriggerKind): Promise { - await telemetry.withTraceId(async () => { - if (!this._isReady) { - return - } - - if (this._currentState instanceof ManualtriggerState) { - if ( - triggerType === vscode.InlineCompletionTriggerKind.Invoke && - this._currentState.hasManualTrigger === false - ) { - this._currentState.hasManualTrigger = true - } - if ( - this.sessionManager.getActiveRecommendation().length > 0 && - this._currentState.hasValidResponse === false - ) { - this._currentState.hasValidResponse = true - } - } - - await this.refresh(vscode.window.activeTextEditor, 'codewhisperer') - }, TelemetryHelper.instance.traceId) + // TODO: this logic will take ~200ms each trigger, need to root cause and re-enable once it's fixed, or it should only be invoked when the tutorial is actually needed + // await telemetry.withTraceId(async () => { + // if (!this._isReady) { + // return + // } + // if (this._currentState instanceof ManualtriggerState) { + // if ( + // triggerType === vscode.InlineCompletionTriggerKind.Invoke && + // this._currentState.hasManualTrigger === false + // ) { + // this._currentState.hasManualTrigger = true + // } + // if ( + // this.sessionManager.getActiveRecommendation().length > 0 && + // this._currentState.hasValidResponse === false + // ) { + // this._currentState.hasValidResponse = true + // } + // } + // await this.refresh(vscode.window.activeTextEditor, 'codewhisperer') + // }, TelemetryHelper.instance.traceId) } isTutorialDone(): boolean { From 69516f4dff84440496110fbfd7ca7f06956b557b Mon Sep 17 00:00:00 2001 From: Lei Gao <97199248+leigaol@users.noreply.github.com> Date: Thu, 24 Jul 2025 16:02:43 -0700 Subject: [PATCH 59/69] fix(amazonq): early stop pagination requests when user decision is made (#7759) ## Problem When a paginated response is in flight, but the user already accepted or rejected a completion, the subsequent paginated requests should be cancelled. This PR also fixes the `codewhispererTotalShownTime` being negative issue by using performance.now() across all timestamp computation. ## Solution This is not a user facing change. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/amazonq/src/app/inline/completion.ts | 7 +++++++ .../amazonq/src/app/inline/recommendationService.ts | 13 +++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/amazonq/src/app/inline/completion.ts b/packages/amazonq/src/app/inline/completion.ts index 9aaf40c4c43..491fe9fb0d2 100644 --- a/packages/amazonq/src/app/inline/completion.ts +++ b/packages/amazonq/src/app/inline/completion.ts @@ -164,6 +164,11 @@ export class InlineCompletionManager implements Disposable { const onInlineRejection = async () => { try { vsCodeState.isCodeWhispererEditing = true + if (this.sessionManager.getActiveSession() === undefined) { + return + } + const requestStartTime = this.sessionManager.getActiveSession()!.requestStartTime + const totalSessionDisplayTime = performance.now() - requestStartTime await commands.executeCommand('editor.action.inlineSuggest.hide') // TODO: also log the seen state for other suggestions in session this.disposable.dispose() @@ -185,6 +190,7 @@ export class InlineCompletionManager implements Disposable { discarded: false, }, }, + totalSessionDisplayTime: totalSessionDisplayTime, } this.languageClient.sendNotification(this.logSessionResultMessageName, params) // clear session manager states once rejected @@ -314,6 +320,7 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem discarded: false, }, }, + totalSessionDisplayTime: performance.now() - prevSession.requestStartTime, } this.languageClient.sendNotification(this.logSessionResultMessageName, params) this.sessionManager.clear() diff --git a/packages/amazonq/src/app/inline/recommendationService.ts b/packages/amazonq/src/app/inline/recommendationService.ts index c7ba5c3b4b7..794d6c46183 100644 --- a/packages/amazonq/src/app/inline/recommendationService.ts +++ b/packages/amazonq/src/app/inline/recommendationService.ts @@ -12,10 +12,10 @@ import { import { CancellationToken, InlineCompletionContext, Position, TextDocument } from 'vscode' import { LanguageClient } from 'vscode-languageclient' import { SessionManager } from './sessionManager' -import { AuthUtil, CodeWhispererStatusBarManager } from 'aws-core-vscode/codewhisperer' +import { AuthUtil, CodeWhispererStatusBarManager, vsCodeState } from 'aws-core-vscode/codewhisperer' import { TelemetryHelper } from './telemetryHelper' import { ICursorUpdateRecorder } from './cursorUpdateManager' -import { globals, getLogger } from 'aws-core-vscode/shared' +import { getLogger } from 'aws-core-vscode/shared' export interface GetAllRecommendationsOptions { emitTelemetry?: boolean @@ -68,7 +68,7 @@ export class RecommendationService { if (options.editsStreakToken) { request = { ...request, partialResultToken: options.editsStreakToken } } - const requestStartTime = globals.clock.Date.now() + const requestStartTime = performance.now() const statusBar = CodeWhispererStatusBarManager.instance // Only track telemetry if enabled @@ -119,7 +119,7 @@ export class RecommendationService { } TelemetryHelper.instance.setFirstSuggestionShowTime() - const firstCompletionDisplayLatency = globals.clock.Date.now() - requestStartTime + const firstCompletionDisplayLatency = performance.now() - requestStartTime this.sessionManager.startSession( result.sessionId, result.items, @@ -186,6 +186,11 @@ export class RecommendationService { request, token ) + // when pagination is in progress, but user has already accepted or rejected an inline completion + // then stop pagination + if (this.sessionManager.getActiveSession() === undefined || vsCodeState.isCodeWhispererEditing) { + break + } this.sessionManager.updateSessionSuggestions(result.items) nextToken = result.partialResultToken } From cf5d9b30d40603bd7b3ae5ce86a3e3c6c9807098 Mon Sep 17 00:00:00 2001 From: Aidan Ton Date: Thu, 24 Jul 2025 16:42:37 -0700 Subject: [PATCH 60/69] fix(amazonq): fix line break format when getting the current text document --- packages/amazonq/src/app/inline/EditRendering/svgGenerator.ts | 2 +- packages/core/src/shared/utilities/diffUtils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/amazonq/src/app/inline/EditRendering/svgGenerator.ts b/packages/amazonq/src/app/inline/EditRendering/svgGenerator.ts index 178045afaee..45a615e318e 100644 --- a/packages/amazonq/src/app/inline/EditRendering/svgGenerator.ts +++ b/packages/amazonq/src/app/inline/EditRendering/svgGenerator.ts @@ -30,7 +30,7 @@ export class SvgGenerationService { origionalCodeHighlightRange: Range[] }> { const textDoc = await vscode.workspace.openTextDocument(filePath) - const originalCode = textDoc.getText() + const originalCode = textDoc.getText().replaceAll('\r\n', '\n') if (originalCode === '') { logger.error(`udiff format error`) throw new ToolkitError('udiff format error') diff --git a/packages/core/src/shared/utilities/diffUtils.ts b/packages/core/src/shared/utilities/diffUtils.ts index 64d09c19036..439c87dd7e6 100644 --- a/packages/core/src/shared/utilities/diffUtils.ts +++ b/packages/core/src/shared/utilities/diffUtils.ts @@ -25,7 +25,7 @@ import jaroWinkler from 'jaro-winkler' */ export async function getPatchedCode(filePath: string, patch: string, snippetMode = false) { const document = await vscode.workspace.openTextDocument(filePath) - const fileContent = document.getText() + const fileContent = document.getText().replaceAll('\r\n', '\n') // Usage with the existing getPatchedCode function: let updatedPatch = patch From 0769acb2ecd3636db62a43761f1393f1ec6a0dc5 Mon Sep 17 00:00:00 2001 From: Lei Gao <97199248+leigaol@users.noreply.github.com> Date: Thu, 24 Jul 2025 23:34:25 -0700 Subject: [PATCH 61/69] fix(amazonq): Faster and more responsive auto trigger UX. (#7763) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem 1. when intelliSense suggestion is surfaced, inline completion are not shown even if provideInlineCompletionItems have returned valid items. 2. provideInlineCompletionItems is unnecessarily debounced at 200ms which creates a user perceivable delay for inline completion UX. We already blocked concurrent API call. 3. The items returned by provideInlineCompletionItems, even though they match the typeahead of the user's current editor (they can be presented), sometimes VS Code decides to not show them to avoid interrupting user's typing flow. This not show suggestion decision is not emitted as API callback or events, causing us to report the suggestion as Rejected but it should be Discard. ## Solution 1. Force the item to render by calling `editor.action.inlineSuggest.trigger` command. Screenshot 2025-07-24 at 7 45 12 PM 3. Remove the debounce 5. Use a specific command with when clause context to detect if a suggestion is shown or not. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- ...-7261a487-e80a-440f-b311-2688e256a886.json | 4 ++ packages/amazonq/package.json | 4 ++ packages/amazonq/src/app/inline/completion.ts | 37 ++++++++++++------- .../amazonq/src/app/inline/sessionManager.ts | 9 +++++ packages/amazonq/src/lsp/client.ts | 4 ++ .../amazonq/apps/inline/completion.test.ts | 4 +- 6 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 packages/amazonq/.changes/next-release/Bug Fix-7261a487-e80a-440f-b311-2688e256a886.json diff --git a/packages/amazonq/.changes/next-release/Bug Fix-7261a487-e80a-440f-b311-2688e256a886.json b/packages/amazonq/.changes/next-release/Bug Fix-7261a487-e80a-440f-b311-2688e256a886.json new file mode 100644 index 00000000000..29d129cc287 --- /dev/null +++ b/packages/amazonq/.changes/next-release/Bug Fix-7261a487-e80a-440f-b311-2688e256a886.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Faster and more responsive inline completion UX" +} diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index 39f4f270406..d791ba05af3 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -975,6 +975,10 @@ "command": "aws.amazonq.showPrev", "when": "inlineSuggestionVisible && !editorReadonly && aws.codewhisperer.connected" }, + { + "command": "aws.amazonq.checkInlineSuggestionVisibility", + "when": "inlineSuggestionVisible && !editorReadonly && aws.codewhisperer.connected" + }, { "command": "aws.amazonq.inline.invokeChat", "win": "ctrl+i", diff --git a/packages/amazonq/src/app/inline/completion.ts b/packages/amazonq/src/app/inline/completion.ts index 491fe9fb0d2..66668be1849 100644 --- a/packages/amazonq/src/app/inline/completion.ts +++ b/packages/amazonq/src/app/inline/completion.ts @@ -2,7 +2,7 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ - +import * as vscode from 'vscode' import { CancellationToken, InlineCompletionContext, @@ -32,7 +32,6 @@ import { ImportAdderProvider, CodeSuggestionsState, vsCodeState, - inlineCompletionsDebounceDelay, noInlineSuggestionsMsg, getDiagnosticsDifferences, getDiagnosticsOfCurrentFile, @@ -42,7 +41,7 @@ import { LineTracker } from './stateTracker/lineTracker' import { InlineTutorialAnnotation } from './tutorials/inlineTutorialAnnotation' import { TelemetryHelper } from './telemetryHelper' import { Experiments, getLogger, sleep } from 'aws-core-vscode/shared' -import { debounce, messageUtils } from 'aws-core-vscode/utils' +import { messageUtils } from 'aws-core-vscode/utils' import { showEdits } from './EditRendering/imageRenderer' import { ICursorUpdateRecorder } from './cursorUpdateManager' import { DocumentEventListener } from './documentEventListener' @@ -214,13 +213,23 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem ) {} private readonly logSessionResultMessageName = 'aws/logInlineCompletionSessionResults' - provideInlineCompletionItems = debounce( - this._provideInlineCompletionItems.bind(this), - inlineCompletionsDebounceDelay, - true - ) - private async _provideInlineCompletionItems( + // Ideally use this API handleDidShowCompletionItem + // https://github.com/microsoft/vscode/blob/main/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts#L83 + // we need this because the returned items of provideInlineCompletionItems may not be actually rendered on screen + // if VS Code believes the user is actively typing then it will not show such item + async checkWhetherInlineCompletionWasShown() { + // this line is to force VS Code to re-render the inline completion + // if it decides the inline completion can be shown + await vscode.commands.executeCommand('editor.action.inlineSuggest.trigger') + // yield event loop to let backend state transition finish plus wait for vsc to render + await sleep(10) + // run the command to detect if inline suggestion is really shown or not + await vscode.commands.executeCommand(`aws.amazonq.checkInlineSuggestionVisibility`) + } + + // this method is automatically invoked by VS Code as user types + async provideInlineCompletionItems( document: TextDocument, position: Position, context: InlineCompletionContext, @@ -307,17 +316,18 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem if (prevItemMatchingPrefix.length > 0) { logstr += `- not call LSP and reuse previous suggestions that match user typed characters - duration between trigger to completion suggestion is displayed ${performance.now() - t0}` + void this.checkWhetherInlineCompletionWasShown() return prevItemMatchingPrefix } - getLogger().debug(`Auto rejecting suggestions from previous session`) - // if no such suggestions, report the previous suggestion as Reject + + // if no such suggestions, report the previous suggestion as Reject or Discarded const params: LogInlineCompletionSessionResultsParams = { sessionId: prevSessionId, completionSessionResult: { [prevItemId]: { - seen: true, + seen: prevSession.displayed, accepted: false, - discarded: false, + discarded: !prevSession.displayed, }, }, totalSessionDisplayTime: performance.now() - prevSession.requestStartTime, @@ -461,6 +471,7 @@ ${itemLog} this.sessionManager.updateCodeReferenceAndImports() // suggestions returned here will be displayed on screen logstr += `- duration between trigger to completion suggestion is displayed: ${performance.now() - t0}ms` + void this.checkWhetherInlineCompletionWasShown() return itemsMatchingTypeahead as InlineCompletionItem[] } catch (e) { getLogger('amazonqLsp').error('Failed to provide completion items: %O', e) diff --git a/packages/amazonq/src/app/inline/sessionManager.ts b/packages/amazonq/src/app/inline/sessionManager.ts index eaa6eaa23b9..7decf035b9a 100644 --- a/packages/amazonq/src/app/inline/sessionManager.ts +++ b/packages/amazonq/src/app/inline/sessionManager.ts @@ -24,6 +24,8 @@ export interface CodeWhispererSession { // partialResultToken for the next trigger if user accepts an EDITS suggestion editsStreakPartialResultToken?: number | string triggerOnAcceptance?: boolean + // whether any suggestion in this session was displayed on screen + displayed: boolean } export class SessionManager { @@ -49,6 +51,7 @@ export class SessionManager { startPosition, firstCompletionDisplayLatency, diagnosticsBeforeAccept, + displayed: false, } this._currentSuggestionIndex = 0 } @@ -128,6 +131,12 @@ export class SessionManager { } } + public checkInlineSuggestionVisibility() { + if (this.activeSession) { + this.activeSession.displayed = true + } + } + private clearReferenceInlineHintsAndImportHints() { ReferenceInlineProvider.instance.removeInlineReference() ImportAdderProvider.instance.clear() diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index 4d052912c8e..e56a4a784b6 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -352,6 +352,10 @@ async function onLanguageServerReady( await vscode.commands.executeCommand('editor.action.inlineSuggest.showNext') sessionManager.onNextSuggestion() }), + // this is a workaround since handleDidShowCompletionItem is not public API + Commands.register('aws.amazonq.checkInlineSuggestionVisibility', async () => { + sessionManager.checkInlineSuggestionVisibility() + }), Commands.register({ id: 'aws.amazonq.invokeInlineCompletion', autoconnect: true }, async () => { await vscode.commands.executeCommand('editor.action.inlineSuggest.trigger') }), diff --git a/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts b/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts index 7b079eaad17..417c8be1426 100644 --- a/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts +++ b/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts @@ -378,7 +378,7 @@ describe('InlineCompletionManager', () => { ) await messageShown }) - describe('debounce behavior', function () { + describe.skip('debounce behavior', function () { let clock: ReturnType beforeEach(function () { @@ -389,7 +389,7 @@ describe('InlineCompletionManager', () => { clock.uninstall() }) - it('should only trigger once on rapid events', async () => { + it.skip('should only trigger once on rapid events', async () => { provider = new AmazonQInlineCompletionItemProvider( languageClient, recommendationService, From 64fae7259c354d469a8597db1658ad9beb54e4aa Mon Sep 17 00:00:00 2001 From: Laxman Reddy <141967714+laileni-aws@users.noreply.github.com> Date: Fri, 25 Jul 2025 09:41:20 -0700 Subject: [PATCH 62/69] refactor(amazonq): Removing unwanted /dev and /doc code (#7760) ## Problem - There is lot of duplicate and unwanted redundant code in [aws-toolkit-vscode](https://github.com/aws/aws-toolkit-vscode) repository. ## Solution - This is the 2nd PR to remove unwanted code. - Here is the 1st PR: https://github.com/aws/aws-toolkit-vscode/pull/7735 --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- .gitignore | 1 - .../session/chatSessionStorage.test.ts | 25 - .../amazonqFeatureDev/session/session.test.ts | 129 - .../unit/amazonqFeatureDev/util/files.test.ts | 172 - .../scripts/build/generateServiceClient.ts | 4 - .../commons/connector/baseMessenger.ts | 219 - .../commons/connector/connectorMessages.ts | 291 - .../commons/session/sessionConfigFactory.ts | 38 - packages/core/src/amazonq/commons/types.ts | 39 - packages/core/src/amazonq/index.ts | 1 - packages/core/src/amazonq/indexNode.ts | 2 - .../core/src/amazonq/session/sessionState.ts | 432 -- packages/core/src/amazonq/util/files.ts | 301 - packages/core/src/amazonq/util/upload.ts | 5 +- packages/core/src/amazonqDoc/app.ts | 103 - packages/core/src/amazonqDoc/constants.ts | 163 - .../amazonqDoc/controllers/chat/controller.ts | 715 --- .../controllers/docGenerationTask.ts | 100 - packages/core/src/amazonqDoc/errors.ts | 63 - packages/core/src/amazonqDoc/index.ts | 10 - packages/core/src/amazonqDoc/messenger.ts | 65 - .../core/src/amazonqDoc/session/session.ts | 371 -- .../src/amazonqDoc/session/sessionState.ts | 165 - .../src/amazonqDoc/storages/chatSession.ts | 23 - packages/core/src/amazonqDoc/types.ts | 83 - .../views/actions/uiMessageListener.ts | 168 - packages/core/src/amazonqFeatureDev/app.ts | 106 - .../codewhispererruntime-2022-11-11.json | 5640 ----------------- .../amazonqFeatureDev/client/featureDev.ts | 376 -- .../core/src/amazonqFeatureDev/constants.ts | 33 - .../controllers/chat/controller.ts | 1089 ---- .../controllers/chat/messenger/constants.ts | 6 - packages/core/src/amazonqFeatureDev/errors.ts | 191 - packages/core/src/amazonqFeatureDev/index.ts | 15 - packages/core/src/amazonqFeatureDev/limits.ts | 14 - packages/core/src/amazonqFeatureDev/models.ts | 12 - .../src/amazonqFeatureDev/session/session.ts | 412 -- .../amazonqFeatureDev/session/sessionState.ts | 285 - .../amazonqFeatureDev/storages/chatSession.ts | 23 - .../src/amazonqFeatureDev/userFacingText.ts | 25 - .../views/actions/uiMessageListener.ts | 169 - .../transformByQ/humanInTheLoopManager.ts | 9 +- .../transformByQ/transformFileHandler.ts | 2 +- packages/core/src/shared/constants.ts | 9 + packages/core/src/shared/errors.ts | 17 +- .../src/shared/utilities/workspaceUtils.ts | 3 +- .../core/src/test/amazonq/common/diff.test.ts | 2 +- .../test/amazonq/session/sessionState.test.ts | 153 - .../src/test/amazonq/session/testSetup.ts | 74 - packages/core/src/test/amazonq/utils.ts | 182 - .../src/test/amazonqDoc/controller.test.ts | 577 -- .../core/src/test/amazonqDoc/mockContent.ts | 86 - .../amazonqDoc/session/sessionState.test.ts | 29 - packages/core/src/test/amazonqDoc/utils.ts | 269 - .../controllers/chat/controller.test.ts | 717 --- .../session/sessionState.test.ts | 95 - packages/core/src/test/index.ts | 1 - .../testInteg/perf/prepareRepoData.test.ts | 84 - .../testInteg/perf/registerNewFiles.test.ts | 89 - 59 files changed, 39 insertions(+), 14443 deletions(-) delete mode 100644 packages/amazonq/test/unit/amazonqFeatureDev/session/chatSessionStorage.test.ts delete mode 100644 packages/amazonq/test/unit/amazonqFeatureDev/session/session.test.ts delete mode 100644 packages/amazonq/test/unit/amazonqFeatureDev/util/files.test.ts delete mode 100644 packages/core/src/amazonq/commons/connector/baseMessenger.ts delete mode 100644 packages/core/src/amazonq/commons/connector/connectorMessages.ts delete mode 100644 packages/core/src/amazonq/commons/session/sessionConfigFactory.ts delete mode 100644 packages/core/src/amazonq/session/sessionState.ts delete mode 100644 packages/core/src/amazonq/util/files.ts delete mode 100644 packages/core/src/amazonqDoc/app.ts delete mode 100644 packages/core/src/amazonqDoc/constants.ts delete mode 100644 packages/core/src/amazonqDoc/controllers/chat/controller.ts delete mode 100644 packages/core/src/amazonqDoc/controllers/docGenerationTask.ts delete mode 100644 packages/core/src/amazonqDoc/errors.ts delete mode 100644 packages/core/src/amazonqDoc/index.ts delete mode 100644 packages/core/src/amazonqDoc/messenger.ts delete mode 100644 packages/core/src/amazonqDoc/session/session.ts delete mode 100644 packages/core/src/amazonqDoc/session/sessionState.ts delete mode 100644 packages/core/src/amazonqDoc/storages/chatSession.ts delete mode 100644 packages/core/src/amazonqDoc/types.ts delete mode 100644 packages/core/src/amazonqDoc/views/actions/uiMessageListener.ts delete mode 100644 packages/core/src/amazonqFeatureDev/app.ts delete mode 100644 packages/core/src/amazonqFeatureDev/client/codewhispererruntime-2022-11-11.json delete mode 100644 packages/core/src/amazonqFeatureDev/client/featureDev.ts delete mode 100644 packages/core/src/amazonqFeatureDev/constants.ts delete mode 100644 packages/core/src/amazonqFeatureDev/controllers/chat/controller.ts delete mode 100644 packages/core/src/amazonqFeatureDev/controllers/chat/messenger/constants.ts delete mode 100644 packages/core/src/amazonqFeatureDev/errors.ts delete mode 100644 packages/core/src/amazonqFeatureDev/index.ts delete mode 100644 packages/core/src/amazonqFeatureDev/limits.ts delete mode 100644 packages/core/src/amazonqFeatureDev/models.ts delete mode 100644 packages/core/src/amazonqFeatureDev/session/session.ts delete mode 100644 packages/core/src/amazonqFeatureDev/session/sessionState.ts delete mode 100644 packages/core/src/amazonqFeatureDev/storages/chatSession.ts delete mode 100644 packages/core/src/amazonqFeatureDev/userFacingText.ts delete mode 100644 packages/core/src/amazonqFeatureDev/views/actions/uiMessageListener.ts delete mode 100644 packages/core/src/test/amazonq/session/sessionState.test.ts delete mode 100644 packages/core/src/test/amazonq/session/testSetup.ts delete mode 100644 packages/core/src/test/amazonq/utils.ts delete mode 100644 packages/core/src/test/amazonqDoc/controller.test.ts delete mode 100644 packages/core/src/test/amazonqDoc/mockContent.ts delete mode 100644 packages/core/src/test/amazonqDoc/session/sessionState.test.ts delete mode 100644 packages/core/src/test/amazonqDoc/utils.ts delete mode 100644 packages/core/src/test/amazonqFeatureDev/controllers/chat/controller.test.ts delete mode 100644 packages/core/src/test/amazonqFeatureDev/session/sessionState.test.ts delete mode 100644 packages/core/src/testInteg/perf/prepareRepoData.test.ts delete mode 100644 packages/core/src/testInteg/perf/registerNewFiles.test.ts diff --git a/.gitignore b/.gitignore index 596af538b2e..3541dbf9cae 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,6 @@ src.gen/* **/src/shared/telemetry/clienttelemetry.d.ts **/src/codewhisperer/client/codewhispererclient.d.ts **/src/codewhisperer/client/codewhispereruserclient.d.ts -**/src/amazonqFeatureDev/client/featuredevproxyclient.d.ts **/src/auth/sso/oidcclientpkce.d.ts # Generated by tests diff --git a/packages/amazonq/test/unit/amazonqFeatureDev/session/chatSessionStorage.test.ts b/packages/amazonq/test/unit/amazonqFeatureDev/session/chatSessionStorage.test.ts deleted file mode 100644 index 4c6073114f8..00000000000 --- a/packages/amazonq/test/unit/amazonqFeatureDev/session/chatSessionStorage.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -import * as assert from 'assert' -import { FeatureDevChatSessionStorage } from 'aws-core-vscode/amazonqFeatureDev' -import { Messenger } from 'aws-core-vscode/amazonq' -import { createMessenger } from 'aws-core-vscode/test' - -describe('chatSession', () => { - const tabID = '1234' - let chatStorage: FeatureDevChatSessionStorage - let messenger: Messenger - - beforeEach(() => { - messenger = createMessenger() - chatStorage = new FeatureDevChatSessionStorage(messenger) - }) - - it('locks getSession', async () => { - const results = await Promise.allSettled([chatStorage.getSession(tabID), chatStorage.getSession(tabID)]) - assert.equal(results.length, 2) - assert.deepStrictEqual(results[0], results[1]) - }) -}) diff --git a/packages/amazonq/test/unit/amazonqFeatureDev/session/session.test.ts b/packages/amazonq/test/unit/amazonqFeatureDev/session/session.test.ts deleted file mode 100644 index 39c38de555f..00000000000 --- a/packages/amazonq/test/unit/amazonqFeatureDev/session/session.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import * as assert from 'assert' - -import sinon from 'sinon' - -import { - ControllerSetup, - createController, - createMessenger, - createSession, - generateVirtualMemoryUri, - sessionRegisterProvider, - sessionWriteFile, - assertTelemetry, -} from 'aws-core-vscode/test' -import { FeatureDevClient, featureDevScheme, FeatureDevCodeGenState } from 'aws-core-vscode/amazonqFeatureDev' -import { Messenger, CurrentWsFolders } from 'aws-core-vscode/amazonq' -import path from 'path' -import { fs } from 'aws-core-vscode/shared' - -describe('session', () => { - const conversationID = '12345' - let messenger: Messenger - - beforeEach(() => { - messenger = createMessenger() - }) - - afterEach(() => { - sinon.restore() - }) - - describe('preloader', () => { - it('emits start chat telemetry', async () => { - const session = await createSession({ messenger, conversationID, scheme: featureDevScheme }) - session.latestMessage = 'implement twosum in typescript' - - await session.preloader() - - assertTelemetry('amazonq_startConversationInvoke', { - amazonqConversationId: conversationID, - }) - }) - }) - describe('insertChanges', async () => { - afterEach(() => { - sinon.restore() - }) - - let workspaceFolderUriFsPath: string - const notRejectedFileName = 'notRejectedFile.js' - const notRejectedFileContent = 'notrejectedFileContent' - let uri: vscode.Uri - let encodedContent: Uint8Array - - async function createCodeGenState() { - const controllerSetup: ControllerSetup = await createController() - - const uploadID = '789' - const tabID = '123' - const workspaceFolders = [controllerSetup.workspaceFolder] as CurrentWsFolders - workspaceFolderUriFsPath = controllerSetup.workspaceFolder.uri.fsPath - uri = generateVirtualMemoryUri(uploadID, notRejectedFileName, featureDevScheme) - - const testConfig = { - conversationId: conversationID, - proxyClient: {} as unknown as FeatureDevClient, - workspaceRoots: [''], - uploadId: uploadID, - workspaceFolders, - } - - const codeGenState = new FeatureDevCodeGenState( - testConfig, - [ - { - zipFilePath: notRejectedFileName, - relativePath: notRejectedFileName, - fileContent: notRejectedFileContent, - rejected: false, - virtualMemoryUri: uri, - workspaceFolder: controllerSetup.workspaceFolder, - changeApplied: false, - }, - { - zipFilePath: 'rejectedFile.js', - relativePath: 'rejectedFile.js', - fileContent: 'rejectedFileContent', - rejected: true, - virtualMemoryUri: generateVirtualMemoryUri(uploadID, 'rejectedFile.js', featureDevScheme), - workspaceFolder: controllerSetup.workspaceFolder, - changeApplied: false, - }, - ], - [], - [], - tabID, - 0, - {} - ) - const session = await createSession({ - messenger, - sessionState: codeGenState, - conversationID, - scheme: featureDevScheme, - }) - encodedContent = new TextEncoder().encode(notRejectedFileContent) - await sessionRegisterProvider(session, uri, encodedContent) - return session - } - it('only insert non rejected files', async () => { - const fsSpyWriteFile = sinon.spy(fs, 'writeFile') - const session = await createCodeGenState() - sinon.stub(session, 'sendLinesOfCodeAcceptedTelemetry').resolves() - await sessionWriteFile(session, uri, encodedContent) - await session.insertChanges() - - const absolutePath = path.join(workspaceFolderUriFsPath, notRejectedFileName) - - assert.ok(fsSpyWriteFile.calledOnce) - assert.ok(fsSpyWriteFile.calledWith(absolutePath, notRejectedFileContent)) - }) - }) -}) diff --git a/packages/amazonq/test/unit/amazonqFeatureDev/util/files.test.ts b/packages/amazonq/test/unit/amazonqFeatureDev/util/files.test.ts deleted file mode 100644 index 574d0a25a19..00000000000 --- a/packages/amazonq/test/unit/amazonqFeatureDev/util/files.test.ts +++ /dev/null @@ -1,172 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -import * as vscode from 'vscode' -import assert from 'assert' -import { - prepareRepoData, - PrepareRepoDataOptions, - TelemetryHelper, - maxRepoSizeBytes, -} from 'aws-core-vscode/amazonqFeatureDev' -import { assertTelemetry, getWorkspaceFolder, TestFolder } from 'aws-core-vscode/test' -import { fs, AmazonqCreateUpload, ZipStream, ContentLengthError } from 'aws-core-vscode/shared' -import { MetricName, Span } from 'aws-core-vscode/telemetry' -import sinon from 'sinon' -import { CodeWhispererSettings } from 'aws-core-vscode/codewhisperer' -import { CurrentWsFolders } from 'aws-core-vscode/amazonq' -import path from 'path' - -const testDevfilePrepareRepo = async (devfileEnabled: boolean) => { - const files: Record = { - 'file.md': 'test content', - // only include when execution is enabled - 'devfile.yaml': 'test', - // .git folder is always dropped (because of vscode global exclude rules) - '.git/ref': '####', - // .gitignore should always be included - '.gitignore': 'node_models/*', - // non code files only when dev execution is enabled - 'abc.jar': 'jar-content', - 'data/logo.ico': 'binary-content', - } - const folder = await TestFolder.create() - - for (const [fileName, content] of Object.entries(files)) { - await folder.write(fileName, content) - } - - const expectedFiles = !devfileEnabled - ? ['file.md', '.gitignore'] - : ['devfile.yaml', 'file.md', '.gitignore', 'abc.jar', 'data/logo.ico'] - - const workspace = getWorkspaceFolder(folder.path) - sinon - .stub(CodeWhispererSettings.instance, 'getAutoBuildSetting') - .returns(devfileEnabled ? { [workspace.uri.fsPath]: true } : {}) - - await testPrepareRepoData([workspace], expectedFiles, { telemetry: new TelemetryHelper() }) -} - -const testPrepareRepoData = async ( - workspaces: vscode.WorkspaceFolder[], - expectedFiles: string[], - prepareRepoDataOptions: PrepareRepoDataOptions, - expectedTelemetryMetrics?: Array<{ metricName: MetricName; value: any }> -) => { - expectedFiles.sort((a, b) => a.localeCompare(b)) - const result = await prepareRepoData( - workspaces.map((ws) => ws.uri.fsPath), - workspaces as CurrentWsFolders, - { - record: () => {}, - } as unknown as Span, - prepareRepoDataOptions - ) - - assert.strictEqual(Buffer.isBuffer(result.zipFileBuffer), true) - // checksum is not the same across different test executions because some unique random folder names are generated - assert.strictEqual(result.zipFileChecksum.length, 44) - - if (expectedTelemetryMetrics) { - for (const metric of expectedTelemetryMetrics) { - assertTelemetry(metric.metricName, metric.value) - } - } - - // Unzip the buffer and compare the entry names - const zipEntries = await ZipStream.unzip(result.zipFileBuffer) - const actualZipEntries = zipEntries.map((entry) => entry.filename) - actualZipEntries.sort((a, b) => a.localeCompare(b)) - assert.deepStrictEqual(actualZipEntries, expectedFiles) -} - -describe('file utils', () => { - describe('prepareRepoData', function () { - const defaultPrepareRepoDataOptions: PrepareRepoDataOptions = { telemetry: new TelemetryHelper() } - - afterEach(() => { - sinon.restore() - }) - - it('returns files in the workspace as a zip', async function () { - const folder = await TestFolder.create() - await folder.write('file1.md', 'test content') - await folder.write('file2.md', 'test content') - await folder.write('docs/infra.svg', 'test content') - const workspace = getWorkspaceFolder(folder.path) - - await testPrepareRepoData([workspace], ['file1.md', 'file2.md'], defaultPrepareRepoDataOptions) - }) - - it('infrastructure diagram is included', async function () { - const folder = await TestFolder.create() - await folder.write('file1.md', 'test content') - await folder.write('file2.svg', 'test content') - await folder.write('docs/infra.svg', 'test content') - const workspace = getWorkspaceFolder(folder.path) - - await testPrepareRepoData([workspace], ['file1.md', 'docs/infra.svg'], { - telemetry: new TelemetryHelper(), - isIncludeInfraDiagram: true, - }) - }) - - it('prepareRepoData ignores denied file extensions', async function () { - const folder = await TestFolder.create() - await folder.write('file.mp4', 'test content') - const workspace = getWorkspaceFolder(folder.path) - - await testPrepareRepoData([workspace], [], defaultPrepareRepoDataOptions, [ - { metricName: 'amazonq_bundleExtensionIgnored', value: { filenameExt: 'mp4', count: 1 } }, - ]) - }) - - it('should ignore devfile.yaml when setting is disabled', async function () { - await testDevfilePrepareRepo(false) - }) - - it('should include devfile.yaml when setting is enabled', async function () { - await testDevfilePrepareRepo(true) - }) - - // Test the logic that allows the customer to modify root source folder - it('prepareRepoData throws a ContentLengthError code when repo is too big', async function () { - const folder = await TestFolder.create() - await folder.write('file.md', 'test content') - const workspace = getWorkspaceFolder(folder.path) - - sinon.stub(fs, 'stat').resolves({ size: 2 * maxRepoSizeBytes } as vscode.FileStat) - await assert.rejects( - () => - prepareRepoData( - [workspace.uri.fsPath], - [workspace], - { - record: () => {}, - } as unknown as Span, - defaultPrepareRepoDataOptions - ), - ContentLengthError - ) - }) - - it('prepareRepoData properly handles multi-root workspaces', async function () { - const folder = await TestFolder.create() - const testFilePath = 'innerFolder/file.md' - await folder.write(testFilePath, 'test content') - - // Add a folder and its subfolder to the workspace - const workspace1 = getWorkspaceFolder(folder.path) - const workspace2 = getWorkspaceFolder(folder.path + '/innerFolder') - const folderName = path.basename(folder.path) - - await testPrepareRepoData( - [workspace1, workspace2], - [`${folderName}_${workspace1.name}/${testFilePath}`], - defaultPrepareRepoDataOptions - ) - }) - }) -}) diff --git a/packages/core/scripts/build/generateServiceClient.ts b/packages/core/scripts/build/generateServiceClient.ts index 7ef217be21b..5d1854527b9 100644 --- a/packages/core/scripts/build/generateServiceClient.ts +++ b/packages/core/scripts/build/generateServiceClient.ts @@ -241,10 +241,6 @@ void (async () => { serviceJsonPath: 'src/codewhisperer/client/user-service-2.json', serviceName: 'CodeWhispererUserClient', }, - { - serviceJsonPath: 'src/amazonqFeatureDev/client/codewhispererruntime-2022-11-11.json', - serviceName: 'FeatureDevProxyClient', - }, ] await generateServiceClients(serviceClientDefinitions) })() diff --git a/packages/core/src/amazonq/commons/connector/baseMessenger.ts b/packages/core/src/amazonq/commons/connector/baseMessenger.ts deleted file mode 100644 index c26834c6fff..00000000000 --- a/packages/core/src/amazonq/commons/connector/baseMessenger.ts +++ /dev/null @@ -1,219 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { ChatItemAction, ProgressField } from '@aws/mynah-ui' -import { AuthFollowUpType, AuthMessageDataMap } from '../../../amazonq/auth/model' -import { i18n } from '../../../shared/i18n-helper' -import { CodeReference } from '../../../amazonq/webview/ui/connector' - -import { MessengerTypes } from '../../../amazonqFeatureDev/controllers/chat/messenger/constants' -import { - AppToWebViewMessageDispatcher, - AsyncEventProgressMessage, - AuthenticationUpdateMessage, - AuthNeededException, - ChatInputEnabledMessage, - ChatMessage, - CodeResultMessage, - FileComponent, - FolderConfirmationMessage, - OpenNewTabMessage, - UpdateAnswerMessage, - UpdatePlaceholderMessage, - UpdatePromptProgressMessage, -} from './connectorMessages' -import { DeletedFileInfo, FollowUpTypes, NewFileInfo } from '../types' -import { messageWithConversationId } from '../../../amazonqFeatureDev/userFacingText' -import { FeatureAuthState } from '../../../codewhisperer/util/authUtil' - -export class Messenger { - public constructor( - private readonly dispatcher: AppToWebViewMessageDispatcher, - private readonly sender: string - ) {} - - public sendAnswer(params: { - message?: string - type: MessengerTypes - followUps?: ChatItemAction[] - tabID: string - canBeVoted?: boolean - snapToTop?: boolean - messageId?: string - disableChatInput?: boolean - }) { - this.dispatcher.sendChatMessage( - new ChatMessage( - { - message: params.message, - messageType: params.type, - followUps: params.followUps, - relatedSuggestions: undefined, - canBeVoted: params.canBeVoted ?? false, - snapToTop: params.snapToTop ?? false, - messageId: params.messageId, - }, - params.tabID, - this.sender - ) - ) - if (params.disableChatInput) { - this.sendChatInputEnabled(params.tabID, false) - } - } - - public sendFeedback(tabID: string) { - this.sendAnswer({ - message: undefined, - type: 'system-prompt', - followUps: [ - { - pillText: i18n('AWS.amazonq.featureDev.pillText.sendFeedback'), - type: FollowUpTypes.SendFeedback, - status: 'info', - }, - ], - tabID, - }) - } - - public sendMonthlyLimitError(tabID: string) { - this.sendAnswer({ - type: 'answer', - tabID: tabID, - message: i18n('AWS.amazonq.featureDev.error.monthlyLimitReached'), - disableChatInput: true, - }) - this.sendUpdatePlaceholder(tabID, i18n('AWS.amazonq.featureDev.placeholder.chatInputDisabled')) - } - - public sendUpdatePromptProgress(tabID: string, progressField: ProgressField | null) { - this.dispatcher.sendUpdatePromptProgress(new UpdatePromptProgressMessage(tabID, this.sender, progressField)) - } - - public sendFolderConfirmationMessage( - tabID: string, - message: string, - folderPath: string, - followUps?: ChatItemAction[] - ) { - this.dispatcher.sendFolderConfirmationMessage( - new FolderConfirmationMessage(tabID, this.sender, message, folderPath, followUps) - ) - - this.sendChatInputEnabled(tabID, false) - } - - public sendErrorMessage( - errorMessage: string, - tabID: string, - retries: number, - conversationId?: string, - showDefaultMessage?: boolean - ) { - if (retries === 0) { - this.sendAnswer({ - type: 'answer', - tabID: tabID, - message: showDefaultMessage ? errorMessage : i18n('AWS.amazonq.featureDev.error.technicalDifficulties'), - canBeVoted: true, - }) - this.sendFeedback(tabID) - return - } - - this.sendAnswer({ - type: 'answer', - tabID: tabID, - message: errorMessage + messageWithConversationId(conversationId), - }) - - this.sendAnswer({ - message: undefined, - type: 'system-prompt', - followUps: [ - { - pillText: i18n('AWS.amazonq.featureDev.pillText.retry'), - type: FollowUpTypes.Retry, - status: 'warning', - }, - ], - tabID, - }) - } - - public sendCodeResult( - filePaths: NewFileInfo[], - deletedFiles: DeletedFileInfo[], - references: CodeReference[], - tabID: string, - uploadId: string, - codeGenerationId: string - ) { - this.dispatcher.sendCodeResult( - new CodeResultMessage(filePaths, deletedFiles, references, tabID, this.sender, uploadId, codeGenerationId) - ) - } - - public sendAsyncEventProgress(tabID: string, inProgress: boolean, message: string | undefined) { - this.dispatcher.sendAsyncEventProgress(new AsyncEventProgressMessage(tabID, this.sender, inProgress, message)) - } - - public updateFileComponent( - tabID: string, - filePaths: NewFileInfo[], - deletedFiles: DeletedFileInfo[], - messageId: string, - disableFileActions: boolean - ) { - this.dispatcher.updateFileComponent( - new FileComponent(tabID, this.sender, filePaths, deletedFiles, messageId, disableFileActions) - ) - } - - public updateChatAnswer(message: UpdateAnswerMessage) { - this.dispatcher.updateChatAnswer(message) - } - - public sendUpdatePlaceholder(tabID: string, newPlaceholder: string) { - this.dispatcher.sendPlaceholder(new UpdatePlaceholderMessage(tabID, this.sender, newPlaceholder)) - } - - public sendChatInputEnabled(tabID: string, enabled: boolean) { - this.dispatcher.sendChatInputEnabled(new ChatInputEnabledMessage(tabID, this.sender, enabled)) - } - - public sendAuthenticationUpdate(enabled: boolean, authenticatingTabIDs: string[]) { - this.dispatcher.sendAuthenticationUpdate( - new AuthenticationUpdateMessage(this.sender, enabled, authenticatingTabIDs) - ) - } - - public async sendAuthNeededExceptionMessage(credentialState: FeatureAuthState, tabID: string) { - let authType: AuthFollowUpType = 'full-auth' - let message = AuthMessageDataMap[authType].message - - switch (credentialState.amazonQ) { - case 'disconnected': - authType = 'full-auth' - message = AuthMessageDataMap[authType].message - break - case 'unsupported': - authType = 'use-supported-auth' - message = AuthMessageDataMap[authType].message - break - case 'expired': - authType = 're-auth' - message = AuthMessageDataMap[authType].message - break - } - - this.dispatcher.sendAuthNeededExceptionMessage(new AuthNeededException(message, authType, tabID, this.sender)) - } - - public openNewTask() { - this.dispatcher.sendOpenNewTask(new OpenNewTabMessage(this.sender)) - } -} diff --git a/packages/core/src/amazonq/commons/connector/connectorMessages.ts b/packages/core/src/amazonq/commons/connector/connectorMessages.ts deleted file mode 100644 index 6f60b786fcb..00000000000 --- a/packages/core/src/amazonq/commons/connector/connectorMessages.ts +++ /dev/null @@ -1,291 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { AuthFollowUpType } from '../../auth/model' -import { MessagePublisher } from '../../messages/messagePublisher' -import { CodeReference } from '../../webview/ui/connector' -import { ChatItemAction, ProgressField, SourceLink } from '@aws/mynah-ui' -import { ChatItemType } from '../model' -import { DeletedFileInfo, NewFileInfo } from '../types' -import { licenseText } from '../../../amazonqFeatureDev/constants' - -class UiMessage { - readonly time: number = Date.now() - readonly type: string = '' - - public constructor( - protected tabID: string, - protected sender: string - ) {} -} - -export class ErrorMessage extends UiMessage { - readonly title!: string - readonly message!: string - override type = 'errorMessage' - - constructor(title: string, message: string, tabID: string, sender: string) { - super(tabID, sender) - this.title = title - this.message = message - } -} - -export class CodeResultMessage extends UiMessage { - readonly message!: string - readonly codeGenerationId!: string - readonly references!: { - information: string - recommendationContentSpan: { - start: number - end: number - } - }[] - readonly conversationID!: string - override type = 'codeResultMessage' - - constructor( - readonly filePaths: NewFileInfo[], - readonly deletedFiles: DeletedFileInfo[], - references: CodeReference[], - tabID: string, - sender: string, - conversationID: string, - codeGenerationId: string - ) { - super(tabID, sender) - this.references = references - .filter((ref) => ref.licenseName && ref.repository && ref.url) - .map((ref) => { - return { - information: licenseText(ref), - - // We're forced to provide these otherwise mynah ui errors somewhere down the line. Though they aren't used - recommendationContentSpan: { - start: 0, - end: 0, - }, - } - }) - this.codeGenerationId = codeGenerationId - this.conversationID = conversationID - } -} - -export class FolderConfirmationMessage extends UiMessage { - readonly folderPath: string - readonly message: string - readonly followUps?: ChatItemAction[] - override type = 'folderConfirmationMessage' - constructor(tabID: string, sender: string, message: string, folderPath: string, followUps?: ChatItemAction[]) { - super(tabID, sender) - this.message = message - this.folderPath = folderPath - this.followUps = followUps - } -} - -export class UpdatePromptProgressMessage extends UiMessage { - readonly progressField: ProgressField | null - override type = 'updatePromptProgress' - constructor(tabID: string, sender: string, progressField: ProgressField | null) { - super(tabID, sender) - this.progressField = progressField - } -} - -export class AsyncEventProgressMessage extends UiMessage { - readonly inProgress: boolean - readonly message: string | undefined - override type = 'asyncEventProgressMessage' - - constructor(tabID: string, sender: string, inProgress: boolean, message: string | undefined) { - super(tabID, sender) - this.inProgress = inProgress - this.message = message - } -} - -export class AuthenticationUpdateMessage { - readonly time: number = Date.now() - readonly type = 'authenticationUpdateMessage' - - constructor( - readonly sender: string, - readonly featureEnabled: boolean, - readonly authenticatingTabIDs: string[] - ) {} -} - -export class FileComponent extends UiMessage { - readonly filePaths: NewFileInfo[] - readonly deletedFiles: DeletedFileInfo[] - override type = 'updateFileComponent' - readonly messageId: string - readonly disableFileActions: boolean - - constructor( - tabID: string, - sender: string, - filePaths: NewFileInfo[], - deletedFiles: DeletedFileInfo[], - messageId: string, - disableFileActions: boolean - ) { - super(tabID, sender) - this.filePaths = filePaths - this.deletedFiles = deletedFiles - this.messageId = messageId - this.disableFileActions = disableFileActions - } -} - -export class UpdatePlaceholderMessage extends UiMessage { - readonly newPlaceholder: string - override type = 'updatePlaceholderMessage' - - constructor(tabID: string, sender: string, newPlaceholder: string) { - super(tabID, sender) - this.newPlaceholder = newPlaceholder - } -} - -export class ChatInputEnabledMessage extends UiMessage { - readonly enabled: boolean - override type = 'chatInputEnabledMessage' - - constructor(tabID: string, sender: string, enabled: boolean) { - super(tabID, sender) - this.enabled = enabled - } -} - -export class OpenNewTabMessage { - readonly time: number = Date.now() - readonly type = 'openNewTabMessage' - - constructor(protected sender: string) {} -} - -export class AuthNeededException extends UiMessage { - readonly message: string - readonly authType: AuthFollowUpType - override type = 'authNeededException' - - constructor(message: string, authType: AuthFollowUpType, tabID: string, sender: string) { - super(tabID, sender) - this.message = message - this.authType = authType - } -} - -export interface ChatMessageProps { - readonly message: string | undefined - readonly messageType: ChatItemType - readonly followUps: ChatItemAction[] | undefined - readonly relatedSuggestions: SourceLink[] | undefined - readonly canBeVoted: boolean - readonly snapToTop: boolean - readonly messageId?: string -} - -export class ChatMessage extends UiMessage { - readonly message: string | undefined - readonly messageType: ChatItemType - readonly followUps: ChatItemAction[] | undefined - readonly relatedSuggestions: SourceLink[] | undefined - readonly canBeVoted: boolean - readonly requestID!: string - readonly snapToTop: boolean - readonly messageId: string | undefined - override type = 'chatMessage' - - constructor(props: ChatMessageProps, tabID: string, sender: string) { - super(tabID, sender) - this.message = props.message - this.messageType = props.messageType - this.followUps = props.followUps - this.relatedSuggestions = props.relatedSuggestions - this.canBeVoted = props.canBeVoted - this.snapToTop = props.snapToTop - this.messageId = props.messageId - } -} - -export interface UpdateAnswerMessageProps { - readonly messageId: string - readonly messageType: ChatItemType - readonly followUps: ChatItemAction[] | undefined -} - -export class UpdateAnswerMessage extends UiMessage { - readonly messageId: string - readonly messageType: ChatItemType - readonly followUps: ChatItemAction[] | undefined - override type = 'updateChatAnswer' - - constructor(props: UpdateAnswerMessageProps, tabID: string, sender: string) { - super(tabID, sender) - this.messageId = props.messageId - this.messageType = props.messageType - this.followUps = props.followUps - } -} - -export class AppToWebViewMessageDispatcher { - constructor(private readonly appsToWebViewMessagePublisher: MessagePublisher) {} - - public sendErrorMessage(message: ErrorMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendChatMessage(message: ChatMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendCodeResult(message: CodeResultMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendUpdatePromptProgress(message: UpdatePromptProgressMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendFolderConfirmationMessage(message: FolderConfirmationMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendAsyncEventProgress(message: AsyncEventProgressMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendPlaceholder(message: UpdatePlaceholderMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendChatInputEnabled(message: ChatInputEnabledMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendAuthNeededExceptionMessage(message: AuthNeededException) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendAuthenticationUpdate(message: AuthenticationUpdateMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public sendOpenNewTask(message: OpenNewTabMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public updateFileComponent(message: FileComponent) { - this.appsToWebViewMessagePublisher.publish(message) - } - - public updateChatAnswer(message: UpdateAnswerMessage) { - this.appsToWebViewMessagePublisher.publish(message) - } -} diff --git a/packages/core/src/amazonq/commons/session/sessionConfigFactory.ts b/packages/core/src/amazonq/commons/session/sessionConfigFactory.ts deleted file mode 100644 index 4204d1d56d6..00000000000 --- a/packages/core/src/amazonq/commons/session/sessionConfigFactory.ts +++ /dev/null @@ -1,38 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import { WorkspaceFolderNotFoundError } from '../../../amazonqFeatureDev/errors' -import { CurrentWsFolders } from '../types' -import { VirtualFileSystem } from '../../../shared/virtualFilesystem' -import { VirtualMemoryFile } from '../../../shared/virtualMemoryFile' - -export interface SessionConfig { - // The paths on disk to where the source code lives - workspaceRoots: string[] - readonly fs: VirtualFileSystem - readonly workspaceFolders: CurrentWsFolders -} - -/** - * Factory method for creating session configurations - * @returns An instantiated SessionConfig, using either the arguments provided or the defaults - */ -export async function createSessionConfig(scheme: string): Promise { - const workspaceFolders = vscode.workspace.workspaceFolders - const firstFolder = workspaceFolders?.[0] - if (workspaceFolders === undefined || workspaceFolders.length === 0 || firstFolder === undefined) { - throw new WorkspaceFolderNotFoundError() - } - - const workspaceRoots = workspaceFolders.map((f) => f.uri.fsPath) - - const fs = new VirtualFileSystem() - - // Register an empty featureDev file that's used when a new file is being added by the LLM - fs.registerProvider(vscode.Uri.from({ scheme, path: 'empty' }), new VirtualMemoryFile(new Uint8Array())) - - return Promise.resolve({ workspaceRoots, fs, workspaceFolders: [firstFolder, ...workspaceFolders.slice(1)] }) -} diff --git a/packages/core/src/amazonq/commons/types.ts b/packages/core/src/amazonq/commons/types.ts index c2d2c427596..3a4d014609d 100644 --- a/packages/core/src/amazonq/commons/types.ts +++ b/packages/core/src/amazonq/commons/types.ts @@ -4,13 +4,8 @@ */ import * as vscode from 'vscode' -import { VirtualFileSystem } from '../../shared/virtualFilesystem' -import type { CancellationTokenSource } from 'vscode' -import { CodeReference, UploadHistory } from '../webview/ui/connector' import { DiffTreeFileInfo } from '../webview/ui/diffTree/types' -import { Messenger } from './connector/baseMessenger' import { FeatureClient } from '../client/client' -import { TelemetryHelper } from '../util/telemetryHelper' import { MynahUI } from '@aws/mynah-ui' export enum FollowUpTypes { @@ -56,12 +51,6 @@ export type Interaction = { responseType?: LLMResponseType } -export interface SessionStateInteraction { - nextState: SessionState | Omit | undefined - interaction: Interaction - currentCodeGenerationId?: string -} - export enum Intent { DEV = 'DEV', DOC = 'DOC', @@ -86,24 +75,6 @@ export type SessionStatePhase = DevPhase.INIT | DevPhase.CODEGEN export type CurrentWsFolders = [vscode.WorkspaceFolder, ...vscode.WorkspaceFolder[]] -export interface SessionState { - readonly filePaths?: NewFileInfo[] - readonly deletedFiles?: DeletedFileInfo[] - readonly references?: CodeReference[] - readonly phase?: SessionStatePhase - readonly uploadId: string - readonly currentIteration?: number - currentCodeGenerationId?: string - tokenSource?: CancellationTokenSource - readonly codeGenerationId?: string - readonly tabID: string - interact(action: SessionStateAction): Promise - updateWorkspaceRoot?: (workspaceRoot: string) => void - codeGenerationRemainingIterationCount?: number - codeGenerationTotalIterationCount?: number - uploadHistory?: UploadHistory -} - export interface SessionStateConfig { workspaceRoots: string[] workspaceFolders: CurrentWsFolders @@ -113,16 +84,6 @@ export interface SessionStateConfig { currentCodeGenerationId?: string } -export interface SessionStateAction { - task: string - msg: string - messenger: Messenger - fs: VirtualFileSystem - telemetry: TelemetryHelper - uploadHistory?: UploadHistory - tokenSource?: CancellationTokenSource -} - export type NewFileZipContents = { zipFilePath: string; fileContent: string } export type NewFileInfo = DiffTreeFileInfo & NewFileZipContents & { diff --git a/packages/core/src/amazonq/index.ts b/packages/core/src/amazonq/index.ts index 3b7737b3547..e06b8ad53d9 100644 --- a/packages/core/src/amazonq/index.ts +++ b/packages/core/src/amazonq/index.ts @@ -36,7 +36,6 @@ export { ChatItemType, referenceLogText } from './commons/model' export { ExtensionMessage } from '../amazonq/webview/ui/commands' export { CodeReference } from '../codewhispererChat/view/connector/connector' export { extractAuthFollowUp } from './util/authUtils' -export { Messenger } from './commons/connector/baseMessenger' export * as secondaryAuth from '../auth/secondaryAuth' export * as authConnection from '../auth/connection' export * as featureConfig from './webview/generators/featureConfig' diff --git a/packages/core/src/amazonq/indexNode.ts b/packages/core/src/amazonq/indexNode.ts index 628b5d626cd..ccc01dc2832 100644 --- a/packages/core/src/amazonq/indexNode.ts +++ b/packages/core/src/amazonq/indexNode.ts @@ -7,6 +7,4 @@ * These agents have underlying requirements on node dependencies (e.g. jsdom, admzip) */ export { init as cwChatAppInit } from '../codewhispererChat/app' -export { init as featureDevChatAppInit } from '../amazonqFeatureDev/app' // TODO: Remove this export { init as gumbyChatAppInit } from '../amazonqGumby/app' -export { init as docChatAppInit } from '../amazonqDoc/app' // TODO: Remove this diff --git a/packages/core/src/amazonq/session/sessionState.ts b/packages/core/src/amazonq/session/sessionState.ts deleted file mode 100644 index 1f206c23159..00000000000 --- a/packages/core/src/amazonq/session/sessionState.ts +++ /dev/null @@ -1,432 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import { ToolkitError } from '../../shared/errors' -import globals from '../../shared/extensionGlobals' -import { getLogger } from '../../shared/logger/logger' -import { AmazonqCreateUpload, Span, telemetry } from '../../shared/telemetry/telemetry' -import { VirtualFileSystem } from '../../shared/virtualFilesystem' -import { CodeReference, UploadHistory } from '../webview/ui/connector' -import { AuthUtil } from '../../codewhisperer/util/authUtil' -import { randomUUID } from '../../shared/crypto' -import { i18n } from '../../shared/i18n-helper' -import { - CodeGenerationStatus, - CurrentWsFolders, - DeletedFileInfo, - DevPhase, - NewFileInfo, - SessionState, - SessionStateAction, - SessionStateConfig, - SessionStateInteraction, - SessionStatePhase, -} from '../commons/types' -import { prepareRepoData, getDeletedFileInfos, registerNewFiles, PrepareRepoDataOptions } from '../util/files' -import { uploadCode } from '../util/upload' -import { truncate } from '../../shared/utilities/textUtilities' - -export const EmptyCodeGenID = 'EMPTY_CURRENT_CODE_GENERATION_ID' -export const RunCommandLogFileName = '.amazonq/dev/run_command.log' - -export interface BaseMessenger { - sendAnswer(params: any): void - sendUpdatePlaceholder?(tabId: string, message: string): void -} - -export abstract class CodeGenBase { - private pollCount = 360 - private requestDelay = 5000 - public tokenSource: vscode.CancellationTokenSource - public phase: SessionStatePhase = DevPhase.CODEGEN - public readonly conversationId: string - public readonly uploadId: string - public currentCodeGenerationId?: string - public isCancellationRequested?: boolean - - constructor( - protected config: SessionStateConfig, - public tabID: string - ) { - this.tokenSource = new vscode.CancellationTokenSource() - this.conversationId = config.conversationId - this.uploadId = config.uploadId - this.currentCodeGenerationId = config.currentCodeGenerationId || EmptyCodeGenID - } - - protected abstract handleProgress(messenger: BaseMessenger, action: SessionStateAction, detail?: string): void - protected abstract getScheme(): string - protected abstract getTimeoutErrorCode(): string - protected abstract handleGenerationComplete( - messenger: BaseMessenger, - newFileInfo: NewFileInfo[], - action: SessionStateAction - ): void - - async generateCode({ - messenger, - fs, - codeGenerationId, - telemetry: telemetry, - workspaceFolders, - action, - }: { - messenger: BaseMessenger - fs: VirtualFileSystem - codeGenerationId: string - telemetry: any - workspaceFolders: CurrentWsFolders - action: SessionStateAction - }): Promise<{ - newFiles: NewFileInfo[] - deletedFiles: DeletedFileInfo[] - references: CodeReference[] - codeGenerationRemainingIterationCount?: number - codeGenerationTotalIterationCount?: number - }> { - let codeGenerationRemainingIterationCount = undefined - let codeGenerationTotalIterationCount = undefined - for ( - let pollingIteration = 0; - pollingIteration < this.pollCount && !this.isCancellationRequested; - ++pollingIteration - ) { - const codegenResult = await this.config.proxyClient.getCodeGeneration(this.conversationId, codeGenerationId) - codeGenerationRemainingIterationCount = codegenResult.codeGenerationRemainingIterationCount - codeGenerationTotalIterationCount = codegenResult.codeGenerationTotalIterationCount - - getLogger().debug(`Codegen response: %O`, codegenResult) - telemetry.setCodeGenerationResult(codegenResult.codeGenerationStatus.status) - - switch (codegenResult.codeGenerationStatus.status as CodeGenerationStatus) { - case CodeGenerationStatus.COMPLETE: { - const { newFileContents, deletedFiles, references } = - await this.config.proxyClient.exportResultArchive(this.conversationId) - - const logFileInfo = newFileContents.find( - (file: { zipFilePath: string; fileContent: string }) => - file.zipFilePath === RunCommandLogFileName - ) - if (logFileInfo) { - logFileInfo.fileContent = truncate(logFileInfo.fileContent, 10000000, '\n... [truncated]') // Limit to max 20MB - getLogger().info(`sessionState: Run Command logs, ${logFileInfo.fileContent}`) - newFileContents.splice(newFileContents.indexOf(logFileInfo), 1) - } - - const newFileInfo = registerNewFiles( - fs, - newFileContents, - this.uploadId, - workspaceFolders, - this.conversationId, - this.getScheme() - ) - telemetry.setNumberOfFilesGenerated(newFileInfo.length) - - this.handleGenerationComplete(messenger, newFileInfo, action) - - return { - newFiles: newFileInfo, - deletedFiles: getDeletedFileInfos(deletedFiles, workspaceFolders), - references, - codeGenerationRemainingIterationCount, - codeGenerationTotalIterationCount, - } - } - case CodeGenerationStatus.PREDICT_READY: - case CodeGenerationStatus.IN_PROGRESS: { - if (codegenResult.codeGenerationStatusDetail) { - this.handleProgress(messenger, action, codegenResult.codeGenerationStatusDetail) - } - await new Promise((f) => globals.clock.setTimeout(f, this.requestDelay)) - break - } - case CodeGenerationStatus.PREDICT_FAILED: - case CodeGenerationStatus.DEBATE_FAILED: - case CodeGenerationStatus.FAILED: { - throw this.handleError(messenger, codegenResult) - } - default: { - const errorMessage = `Unknown status: ${codegenResult.codeGenerationStatus.status}\n` - throw new ToolkitError(errorMessage, { code: 'UnknownCodeGenError' }) - } - } - } - - if (!this.isCancellationRequested) { - const errorMessage = i18n('AWS.amazonq.featureDev.error.codeGen.timeout') - throw new ToolkitError(errorMessage, { code: this.getTimeoutErrorCode() }) - } - - return { - newFiles: [], - deletedFiles: [], - references: [], - codeGenerationRemainingIterationCount: codeGenerationRemainingIterationCount, - codeGenerationTotalIterationCount: codeGenerationTotalIterationCount, - } - } - - protected abstract handleError(messenger: BaseMessenger, codegenResult: any): Error -} - -export abstract class BasePrepareCodeGenState implements SessionState { - public tokenSource: vscode.CancellationTokenSource - public readonly phase = DevPhase.CODEGEN - public uploadId: string - public conversationId: string - - constructor( - protected config: SessionStateConfig, - public filePaths: NewFileInfo[], - public deletedFiles: DeletedFileInfo[], - public references: CodeReference[], - public tabID: string, - public currentIteration: number, - public codeGenerationRemainingIterationCount?: number, - public codeGenerationTotalIterationCount?: number, - public uploadHistory: UploadHistory = {}, - public superTokenSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource(), - public currentCodeGenerationId?: string, - public codeGenerationId?: string - ) { - this.tokenSource = superTokenSource || new vscode.CancellationTokenSource() - this.uploadId = config.uploadId - this.currentCodeGenerationId = currentCodeGenerationId - this.conversationId = config.conversationId - this.uploadHistory = uploadHistory - this.codeGenerationId = codeGenerationId - } - - updateWorkspaceRoot(workspaceRoot: string) { - this.config.workspaceRoots = [workspaceRoot] - } - - protected createNextState( - config: SessionStateConfig, - StateClass?: new ( - config: SessionStateConfig, - filePaths: NewFileInfo[], - deletedFiles: DeletedFileInfo[], - references: CodeReference[], - tabID: string, - currentIteration: number, - uploadHistory: UploadHistory, - codeGenerationRemainingIterationCount?: number, - codeGenerationTotalIterationCount?: number - ) => SessionState - ): SessionState { - return new StateClass!( - config, - this.filePaths, - this.deletedFiles, - this.references, - this.tabID, - this.currentIteration, - this.uploadHistory - ) - } - - protected abstract preUpload(action: SessionStateAction): void - protected abstract postUpload(action: SessionStateAction): void - - async interact(action: SessionStateAction): Promise { - this.preUpload(action) - const uploadId = await telemetry.amazonq_createUpload.run(async (span) => { - span.record({ - amazonqConversationId: this.config.conversationId, - credentialStartUrl: AuthUtil.instance.startUrl, - }) - const { zipFileBuffer, zipFileChecksum } = await this.prepareProjectZip( - this.config.workspaceRoots, - this.config.workspaceFolders, - span, - { telemetry: action.telemetry } - ) - const uploadId = randomUUID() - const { uploadUrl, kmsKeyArn } = await this.config.proxyClient.createUploadUrl( - this.config.conversationId, - zipFileChecksum, - zipFileBuffer.length, - uploadId - ) - - await uploadCode(uploadUrl, zipFileBuffer, zipFileChecksum, kmsKeyArn) - this.postUpload(action) - - return uploadId - }) - - this.uploadId = uploadId - const nextState = this.createNextState({ ...this.config, uploadId }) - return nextState.interact(action) - } - - protected async prepareProjectZip( - workspaceRoots: string[], - workspaceFolders: CurrentWsFolders, - span: Span, - options: PrepareRepoDataOptions - ) { - return await prepareRepoData(workspaceRoots, workspaceFolders, span, options) - } -} - -export interface CodeGenerationParams { - messenger: BaseMessenger - fs: VirtualFileSystem - codeGenerationId: string - telemetry: any - workspaceFolders: CurrentWsFolders -} - -export interface CreateNextStateParams { - filePaths: NewFileInfo[] - deletedFiles: DeletedFileInfo[] - references: CodeReference[] - currentIteration: number - remainingIterations?: number - totalIterations?: number - uploadHistory: UploadHistory - tokenSource: vscode.CancellationTokenSource - currentCodeGenerationId?: string - codeGenerationId?: string -} - -export abstract class BaseCodeGenState extends CodeGenBase implements SessionState { - constructor( - config: SessionStateConfig, - public filePaths: NewFileInfo[], - public deletedFiles: DeletedFileInfo[], - public references: CodeReference[], - tabID: string, - public currentIteration: number, - public uploadHistory: UploadHistory, - public codeGenerationRemainingIterationCount?: number, - public codeGenerationTotalIterationCount?: number - ) { - super(config, tabID) - } - - protected createNextState( - config: SessionStateConfig, - params: CreateNextStateParams, - StateClass?: new ( - config: SessionStateConfig, - filePaths: NewFileInfo[], - deletedFiles: DeletedFileInfo[], - references: CodeReference[], - tabID: string, - currentIteration: number, - remainingIterations?: number, - totalIterations?: number, - uploadHistory?: UploadHistory, - tokenSource?: vscode.CancellationTokenSource, - currentCodeGenerationId?: string, - codeGenerationId?: string - ) => SessionState - ): SessionState { - return new StateClass!( - config, - params.filePaths, - params.deletedFiles, - params.references, - this.tabID, - params.currentIteration, - params.remainingIterations, - params.totalIterations, - params.uploadHistory, - params.tokenSource, - params.currentCodeGenerationId, - params.codeGenerationId - ) - } - - async interact(action: SessionStateAction): Promise { - return telemetry.amazonq_codeGenerationInvoke.run(async (span) => { - try { - action.tokenSource?.token.onCancellationRequested(() => { - this.isCancellationRequested = true - if (action.tokenSource) { - this.tokenSource = action.tokenSource - } - }) - - span.record({ - amazonqConversationId: this.config.conversationId, - credentialStartUrl: AuthUtil.instance.startUrl, - }) - - action.telemetry.setGenerateCodeIteration(this.currentIteration) - action.telemetry.setGenerateCodeLastInvocationTime() - - const codeGenerationId = randomUUID() - await this.startCodeGeneration(action, codeGenerationId) - - const codeGeneration = await this.generateCode({ - messenger: action.messenger, - fs: action.fs, - codeGenerationId, - telemetry: action.telemetry, - workspaceFolders: this.config.workspaceFolders, - action, - }) - - if (codeGeneration && !action.tokenSource?.token.isCancellationRequested) { - this.config.currentCodeGenerationId = codeGenerationId - this.currentCodeGenerationId = codeGenerationId - } - - this.filePaths = codeGeneration.newFiles - this.deletedFiles = codeGeneration.deletedFiles - this.references = codeGeneration.references - this.codeGenerationRemainingIterationCount = codeGeneration.codeGenerationRemainingIterationCount - this.codeGenerationTotalIterationCount = codeGeneration.codeGenerationTotalIterationCount - this.currentIteration = - this.codeGenerationRemainingIterationCount && this.codeGenerationTotalIterationCount - ? this.codeGenerationTotalIterationCount - this.codeGenerationRemainingIterationCount - : this.currentIteration + 1 - - if (action.uploadHistory && !action.uploadHistory[codeGenerationId] && codeGenerationId) { - action.uploadHistory[codeGenerationId] = { - timestamp: Date.now(), - uploadId: this.config.uploadId, - filePaths: codeGeneration.newFiles, - deletedFiles: codeGeneration.deletedFiles, - tabId: this.tabID, - } - } - - action.telemetry.setAmazonqNumberOfReferences(this.references.length) - action.telemetry.recordUserCodeGenerationTelemetry(span, this.conversationId) - - const nextState = this.createNextState(this.config, { - filePaths: this.filePaths, - deletedFiles: this.deletedFiles, - references: this.references, - currentIteration: this.currentIteration, - remainingIterations: this.codeGenerationRemainingIterationCount, - totalIterations: this.codeGenerationTotalIterationCount, - uploadHistory: action.uploadHistory ? action.uploadHistory : {}, - tokenSource: this.tokenSource, - currentCodeGenerationId: this.currentCodeGenerationId, - codeGenerationId, - }) - - return { - nextState, - interaction: {}, - } - } catch (e) { - throw e instanceof ToolkitError - ? e - : ToolkitError.chain(e, 'Server side error', { code: 'UnhandledCodeGenServerSideError' }) - } - }) - } - - protected abstract startCodeGeneration(action: SessionStateAction, codeGenerationId: string): Promise -} diff --git a/packages/core/src/amazonq/util/files.ts b/packages/core/src/amazonq/util/files.ts deleted file mode 100644 index afa0b674928..00000000000 --- a/packages/core/src/amazonq/util/files.ts +++ /dev/null @@ -1,301 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import * as path from 'path' -import { - collectFiles, - CollectFilesFilter, - defaultExcludePatterns, - getWorkspaceFoldersByPrefixes, -} from '../../shared/utilities/workspaceUtils' - -import { PrepareRepoFailedError } from '../../amazonqFeatureDev/errors' -import { getLogger } from '../../shared/logger/logger' -import { maxFileSizeBytes } from '../../amazonqFeatureDev/limits' -import { CurrentWsFolders, DeletedFileInfo, NewFileInfo, NewFileZipContents } from '../../amazonqDoc/types' -import { ContentLengthError, hasCode, ToolkitError } from '../../shared/errors' -import { AmazonqCreateUpload, Span, telemetry as amznTelemetry, telemetry } from '../../shared/telemetry/telemetry' -import { maxRepoSizeBytes } from '../../amazonqFeatureDev/constants' -import { isCodeFile } from '../../shared/filetypes' -import { fs } from '../../shared/fs/fs' -import { VirtualFileSystem } from '../../shared/virtualFilesystem' -import { VirtualMemoryFile } from '../../shared/virtualMemoryFile' -import { CodeWhispererSettings } from '../../codewhisperer/util/codewhispererSettings' -import { ZipStream } from '../../shared/utilities/zipStream' -import { isPresent } from '../../shared/utilities/collectionUtils' -import { AuthUtil } from '../../codewhisperer/util/authUtil' -import { TelemetryHelper } from '../util/telemetryHelper' - -export const SvgFileExtension = '.svg' - -export async function checkForDevFile(root: string) { - const devFilePath = root + '/devfile.yaml' - const hasDevFile = await fs.existsFile(devFilePath) - return hasDevFile -} - -function isInfraDiagramFile(relativePath: string) { - return ( - relativePath.toLowerCase().endsWith(path.join('docs', 'infra.dot')) || - relativePath.toLowerCase().endsWith(path.join('docs', 'infra.svg')) - ) -} - -export type PrepareRepoDataOptions = { - telemetry?: TelemetryHelper - zip?: ZipStream - isIncludeInfraDiagram?: boolean -} - -/** - * given the root path of the repo it zips its files in memory and generates a checksum for it. - */ -export async function prepareRepoData( - repoRootPaths: string[], - workspaceFolders: CurrentWsFolders, - span: Span, - options?: PrepareRepoDataOptions -) { - try { - const telemetry = options?.telemetry - const isIncludeInfraDiagram = options?.isIncludeInfraDiagram ?? false - const zip = options?.zip ?? new ZipStream() - - const autoBuildSetting = CodeWhispererSettings.instance.getAutoBuildSetting() - const useAutoBuildFeature = autoBuildSetting[repoRootPaths[0]] ?? false - const excludePatterns: string[] = [] - let filterFn: CollectFilesFilter | undefined = undefined - - // We only respect gitignore file rules if useAutoBuildFeature is on, this is to avoid dropping necessary files for building the code (e.g. png files imported in js code) - if (!useAutoBuildFeature) { - if (isIncludeInfraDiagram) { - // ensure svg is not filtered out by files search - excludePatterns.push(...defaultExcludePatterns.filter((p) => !p.endsWith(SvgFileExtension))) - // ensure only infra diagram is included from all svg files - filterFn = (relativePath: string) => { - if (!relativePath.toLowerCase().endsWith(SvgFileExtension)) { - return false - } - return !isInfraDiagramFile(relativePath) - } - } else { - excludePatterns.push(...defaultExcludePatterns) - } - } - - const files = await collectFiles(repoRootPaths, workspaceFolders, { - maxTotalSizeBytes: maxRepoSizeBytes, - excludeByGitIgnore: true, - excludePatterns: excludePatterns, - filterFn: filterFn, - }) - - let totalBytes = 0 - const ignoredExtensionMap = new Map() - for (const file of files) { - let fileSize - try { - fileSize = (await fs.stat(file.fileUri)).size - } catch (error) { - if (hasCode(error) && error.code === 'ENOENT') { - // No-op: Skip if file does not exist - continue - } - throw error - } - const isCodeFile_ = isCodeFile(file.relativeFilePath) - const isDevFile = file.relativeFilePath === 'devfile.yaml' - const isInfraDiagramFileExt = isInfraDiagramFile(file.relativeFilePath) - - let isExcludeFile = fileSize >= maxFileSizeBytes - // When useAutoBuildFeature is on, only respect the gitignore rules filtered earlier and apply the size limit - if (!isExcludeFile && !useAutoBuildFeature) { - isExcludeFile = isDevFile || (!isCodeFile_ && (!isIncludeInfraDiagram || !isInfraDiagramFileExt)) - } - - if (isExcludeFile) { - if (!isCodeFile_) { - const re = /(?:\.([^.]+))?$/ - const extensionArray = re.exec(file.relativeFilePath) - const extension = extensionArray?.length ? extensionArray[1] : undefined - if (extension) { - const currentCount = ignoredExtensionMap.get(extension) - - ignoredExtensionMap.set(extension, (currentCount ?? 0) + 1) - } - } - continue - } - - totalBytes += fileSize - // Paths in zip should be POSIX compliant regardless of OS - // Reference: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT - const posixPath = file.zipFilePath.split(path.sep).join(path.posix.sep) - - try { - zip.writeFile(file.fileUri.fsPath, posixPath) - } catch (error) { - if (error instanceof Error && error.message.includes('File not found')) { - // No-op: Skip if file was deleted or does not exist - // Reference: https://github.com/cthackers/adm-zip/blob/1cd32f7e0ad3c540142a76609bb538a5cda2292f/adm-zip.js#L296-L321 - continue - } - throw error - } - } - - const iterator = ignoredExtensionMap.entries() - - for (let i = 0; i < ignoredExtensionMap.size; i++) { - const iteratorValue = iterator.next().value - if (iteratorValue) { - const [key, value] = iteratorValue - await amznTelemetry.amazonq_bundleExtensionIgnored.run(async (bundleSpan) => { - const event = { - filenameExt: key, - count: value, - } - - bundleSpan.record(event) - }) - } - } - - if (telemetry) { - telemetry.setRepositorySize(totalBytes) - } - - span.record({ amazonqRepositorySize: totalBytes }) - const zipResult = await zip.finalize() - - const zipFileBuffer = zipResult.streamBuffer.getContents() || Buffer.from('') - return { - zipFileBuffer, - zipFileChecksum: zipResult.hash, - } - } catch (error) { - getLogger().debug(`Failed to prepare repo: ${error}`) - if (error instanceof ToolkitError && error.code === 'ContentLengthError') { - throw new ContentLengthError(error.message) - } - throw new PrepareRepoFailedError() - } -} - -/** - * gets the absolute path from a zip path - * @param zipFilePath the path in the zip file - * @param workspacesByPrefix the workspaces with generated prefixes - * @param workspaceFolders all workspace folders - * @returns all possible path info - */ -export function getPathsFromZipFilePath( - zipFilePath: string, - workspacesByPrefix: { [prefix: string]: vscode.WorkspaceFolder } | undefined, - workspaceFolders: CurrentWsFolders -): { - absolutePath: string - relativePath: string - workspaceFolder: vscode.WorkspaceFolder -} { - // when there is just a single workspace folder, there is no prefixing - if (workspacesByPrefix === undefined) { - return { - absolutePath: path.join(workspaceFolders[0].uri.fsPath, zipFilePath), - relativePath: zipFilePath, - workspaceFolder: workspaceFolders[0], - } - } - // otherwise the first part of the zipPath is the prefix - const prefix = zipFilePath.substring(0, zipFilePath.indexOf(path.sep)) - const workspaceFolder = - workspacesByPrefix[prefix] ?? - (workspacesByPrefix[Object.values(workspacesByPrefix).find((val) => val.index === 0)?.name ?? ''] || undefined) - if (workspaceFolder === undefined) { - throw new ToolkitError(`Could not find workspace folder for prefix ${prefix}`) - } - return { - absolutePath: path.join(workspaceFolder.uri.fsPath, zipFilePath.substring(prefix.length + 1)), - relativePath: zipFilePath.substring(prefix.length + 1), - workspaceFolder, - } -} - -export function getDeletedFileInfos(deletedFiles: string[], workspaceFolders: CurrentWsFolders): DeletedFileInfo[] { - const workspaceFolderPrefixes = getWorkspaceFoldersByPrefixes(workspaceFolders) - return deletedFiles - .map((deletedFilePath) => { - const prefix = - workspaceFolderPrefixes === undefined - ? '' - : deletedFilePath.substring(0, deletedFilePath.indexOf(path.sep)) - const folder = workspaceFolderPrefixes === undefined ? workspaceFolders[0] : workspaceFolderPrefixes[prefix] - if (folder === undefined) { - getLogger().error(`No workspace folder found for file: ${deletedFilePath} and prefix: ${prefix}`) - return undefined - } - const prefixLength = workspaceFolderPrefixes === undefined ? 0 : prefix.length + 1 - return { - zipFilePath: deletedFilePath, - workspaceFolder: folder, - relativePath: deletedFilePath.substring(prefixLength), - rejected: false, - changeApplied: false, - } - }) - .filter(isPresent) -} - -export function registerNewFiles( - fs: VirtualFileSystem, - newFileContents: NewFileZipContents[], - uploadId: string, - workspaceFolders: CurrentWsFolders, - conversationId: string, - scheme: string -): NewFileInfo[] { - const result: NewFileInfo[] = [] - const workspaceFolderPrefixes = getWorkspaceFoldersByPrefixes(workspaceFolders) - for (const { zipFilePath, fileContent } of newFileContents) { - const encoder = new TextEncoder() - const contents = encoder.encode(fileContent) - const generationFilePath = path.join(uploadId, zipFilePath) - const uri = vscode.Uri.from({ scheme, path: generationFilePath }) - fs.registerProvider(uri, new VirtualMemoryFile(contents)) - const prefix = - workspaceFolderPrefixes === undefined ? '' : zipFilePath.substring(0, zipFilePath.indexOf(path.sep)) - const folder = - workspaceFolderPrefixes === undefined - ? workspaceFolders[0] - : (workspaceFolderPrefixes[prefix] ?? - workspaceFolderPrefixes[ - Object.values(workspaceFolderPrefixes).find((val) => val.index === 0)?.name ?? '' - ]) - if (folder === undefined) { - telemetry.toolkit_trackScenario.emit({ - count: 1, - amazonqConversationId: conversationId, - credentialStartUrl: AuthUtil.instance.startUrl, - scenario: 'wsOrphanedDocuments', - }) - getLogger().error(`No workspace folder found for file: ${zipFilePath} and prefix: ${prefix}`) - continue - } - result.push({ - zipFilePath, - fileContent, - virtualMemoryUri: uri, - workspaceFolder: folder, - relativePath: zipFilePath.substring( - workspaceFolderPrefixes === undefined ? 0 : prefix.length > 0 ? prefix.length + 1 : 0 - ), - rejected: false, - changeApplied: false, - }) - } - - return result -} diff --git a/packages/core/src/amazonq/util/upload.ts b/packages/core/src/amazonq/util/upload.ts index bd4ff26cc45..92e88fbea0e 100644 --- a/packages/core/src/amazonq/util/upload.ts +++ b/packages/core/src/amazonq/util/upload.ts @@ -5,11 +5,10 @@ import request, { RequestError } from '../../shared/request' import { getLogger } from '../../shared/logger/logger' -import { featureName } from '../../amazonqFeatureDev/constants' -import { UploadCodeError, UploadURLExpired } from '../../amazonqFeatureDev/errors' -import { ToolkitError } from '../../shared/errors' +import { ToolkitError, UploadCodeError, UploadURLExpired } from '../../shared/errors' import { i18n } from '../../shared/i18n-helper' +import { featureName } from '../../shared/constants' /** * uploadCode diff --git a/packages/core/src/amazonqDoc/app.ts b/packages/core/src/amazonqDoc/app.ts deleted file mode 100644 index 52985b82a00..00000000000 --- a/packages/core/src/amazonqDoc/app.ts +++ /dev/null @@ -1,103 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import { ChatControllerEventEmitters, DocController } from './controllers/chat/controller' -import { AmazonQAppInitContext } from '../amazonq/apps/initContext' -import { MessageListener } from '../amazonq/messages/messageListener' -import { fromQueryToParameters } from '../shared/utilities/uriUtils' -import { getLogger } from '../shared/logger/logger' -import { AuthUtil } from '../codewhisperer/util/authUtil' -import { debounce } from 'lodash' -import { DocChatSessionStorage } from './storages/chatSession' -import { UIMessageListener } from './views/actions/uiMessageListener' -import globals from '../shared/extensionGlobals' -import { AppToWebViewMessageDispatcher } from '../amazonq/commons/connector/connectorMessages' -import { docChat, docScheme } from './constants' -import { TabIdNotFoundError } from '../amazonqFeatureDev/errors' -import { DocMessenger } from './messenger' - -export function init(appContext: AmazonQAppInitContext) { - const docChatControllerEventEmitters: ChatControllerEventEmitters = { - processHumanChatMessage: new vscode.EventEmitter(), - followUpClicked: new vscode.EventEmitter(), - openDiff: new vscode.EventEmitter(), - processChatItemVotedMessage: new vscode.EventEmitter(), - stopResponse: new vscode.EventEmitter(), - tabOpened: new vscode.EventEmitter(), - processChatItemFeedbackMessage: new vscode.EventEmitter(), - tabClosed: new vscode.EventEmitter(), - authClicked: new vscode.EventEmitter(), - formActionClicked: new vscode.EventEmitter(), - processResponseBodyLinkClick: new vscode.EventEmitter(), - insertCodeAtPositionClicked: new vscode.EventEmitter(), - fileClicked: new vscode.EventEmitter(), - } - - const messenger = new DocMessenger( - new AppToWebViewMessageDispatcher(appContext.getAppsToWebViewMessagePublisher()), - docChat - ) - const sessionStorage = new DocChatSessionStorage(messenger) - - new DocController( - docChatControllerEventEmitters, - messenger, - sessionStorage, - appContext.onDidChangeAmazonQVisibility.event - ) - - const docProvider = new (class implements vscode.TextDocumentContentProvider { - async provideTextDocumentContent(uri: vscode.Uri): Promise { - const params = fromQueryToParameters(uri.query) - - const tabID = params.get('tabID') - if (!tabID) { - getLogger().error(`Unable to find tabID from ${uri.toString()}`) - throw new TabIdNotFoundError() - } - - const session = await sessionStorage.getSession(tabID) - const content = await session.config.fs.readFile(uri) - const decodedContent = new TextDecoder().decode(content) - return decodedContent - } - })() - - const textDocumentProvider = vscode.workspace.registerTextDocumentContentProvider(docScheme, docProvider) - - globals.context.subscriptions.push(textDocumentProvider) - - const docChatUIInputEventEmitter = new vscode.EventEmitter() - - new UIMessageListener({ - chatControllerEventEmitters: docChatControllerEventEmitters, - webViewMessageListener: new MessageListener(docChatUIInputEventEmitter), - }) - - const debouncedEvent = debounce(async () => { - const authenticated = (await AuthUtil.instance.getChatAuthState()).amazonQ === 'connected' - let authenticatingSessionIDs: string[] = [] - if (authenticated) { - const authenticatingSessions = sessionStorage.getAuthenticatingSessions() - - authenticatingSessionIDs = authenticatingSessions.map((session: any) => session.tabID) - - // We've already authenticated these sessions - for (const session of authenticatingSessions) { - session.isAuthenticating = false - } - } - - messenger.sendAuthenticationUpdate(authenticated, authenticatingSessionIDs) - }, 500) - - AuthUtil.instance.secondaryAuth.onDidChangeActiveConnection(() => { - return debouncedEvent() - }) - AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(() => { - return debouncedEvent() - }) -} diff --git a/packages/core/src/amazonqDoc/constants.ts b/packages/core/src/amazonqDoc/constants.ts deleted file mode 100644 index 7b57e7c2ce9..00000000000 --- a/packages/core/src/amazonqDoc/constants.ts +++ /dev/null @@ -1,163 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { MynahIcons, Status } from '@aws/mynah-ui' -import { FollowUpTypes } from '../amazonq/commons/types' -import { NewFileInfo } from './types' -import { i18n } from '../shared/i18n-helper' - -// For uniquely identifiying which chat messages should be routed to Doc -export const docChat = 'docChat' - -export const docScheme = 'aws-doc' - -export const featureName = 'Amazon Q Doc Generation' - -export function getFileSummaryPercentage(input: string): number { - // Split the input string by newline characters - const lines = input.split('\n') - - // Find the line containing "summarized:" - const summaryLine = lines.find((line) => line.includes('summarized:')) - - // If the line is not found, return null - if (!summaryLine) { - return -1 - } - - // Extract the numbers from the summary line - const [summarized, total] = summaryLine.split(':')[1].trim().split(' of ').map(Number) - - // Calculate the percentage - const percentage = (summarized / total) * 100 - - return percentage -} - -const checkIcons = { - wait: '☐', - current: '☐', - done: '☑', -} - -const getIconForStep = (targetStep: number, currentStep: number) => { - return currentStep === targetStep - ? checkIcons.current - : currentStep > targetStep - ? checkIcons.done - : checkIcons.wait -} - -export enum DocGenerationStep { - UPLOAD_TO_S3, - SUMMARIZING_FILES, - GENERATING_ARTIFACTS, -} - -export const docGenerationProgressMessage = (currentStep: DocGenerationStep, mode: Mode) => ` -${mode === Mode.CREATE ? i18n('AWS.amazonq.doc.answer.creating') : i18n('AWS.amazonq.doc.answer.updating')} - -${getIconForStep(DocGenerationStep.UPLOAD_TO_S3, currentStep)} ${i18n('AWS.amazonq.doc.answer.scanning')} - -${getIconForStep(DocGenerationStep.SUMMARIZING_FILES, currentStep)} ${i18n('AWS.amazonq.doc.answer.summarizing')} - -${getIconForStep(DocGenerationStep.GENERATING_ARTIFACTS, currentStep)} ${i18n('AWS.amazonq.doc.answer.generating')} - - -` - -export const docGenerationSuccessMessage = (mode: Mode) => - mode === Mode.CREATE ? i18n('AWS.amazonq.doc.answer.readmeCreated') : i18n('AWS.amazonq.doc.answer.readmeUpdated') - -export const docRejectConfirmation = 'Your changes have been discarded.' - -export const FolderSelectorFollowUps = [ - { - icon: 'ok' as MynahIcons, - pillText: 'Yes', - prompt: 'Yes', - status: 'success' as Status, - type: FollowUpTypes.ProceedFolderSelection, - }, - { - icon: 'refresh' as MynahIcons, - pillText: 'Change folder', - prompt: 'Change folder', - status: 'info' as Status, - type: FollowUpTypes.ChooseFolder, - }, - { - icon: 'cancel' as MynahIcons, - pillText: 'Cancel', - prompt: 'Cancel', - status: 'error' as Status, - type: FollowUpTypes.CancelFolderSelection, - }, -] - -export const CodeChangeFollowUps = [ - { - pillText: i18n('AWS.amazonq.doc.pillText.accept'), - prompt: i18n('AWS.amazonq.doc.pillText.accept'), - type: FollowUpTypes.AcceptChanges, - icon: 'ok' as MynahIcons, - status: 'success' as Status, - }, - { - pillText: i18n('AWS.amazonq.doc.pillText.makeChanges'), - prompt: i18n('AWS.amazonq.doc.pillText.makeChanges'), - type: FollowUpTypes.MakeChanges, - icon: 'refresh' as MynahIcons, - status: 'info' as Status, - }, - { - pillText: i18n('AWS.amazonq.doc.pillText.reject'), - prompt: i18n('AWS.amazonq.doc.pillText.reject'), - type: FollowUpTypes.RejectChanges, - icon: 'cancel' as MynahIcons, - status: 'error' as Status, - }, -] - -export const NewSessionFollowUps = [ - { - pillText: i18n('AWS.amazonq.doc.pillText.newTask'), - type: FollowUpTypes.NewTask, - status: 'info' as Status, - }, - { - pillText: i18n('AWS.amazonq.doc.pillText.closeSession'), - type: FollowUpTypes.CloseSession, - status: 'info' as Status, - }, -] - -export const SynchronizeDocumentation = { - pillText: i18n('AWS.amazonq.doc.pillText.update'), - prompt: i18n('AWS.amazonq.doc.pillText.update'), - type: FollowUpTypes.SynchronizeDocumentation, -} - -export const EditDocumentation = { - pillText: i18n('AWS.amazonq.doc.pillText.makeChange'), - prompt: i18n('AWS.amazonq.doc.pillText.makeChange'), - type: FollowUpTypes.EditDocumentation, -} - -export enum Mode { - NONE = 'None', - CREATE = 'Create', - SYNC = 'Sync', - EDIT = 'Edit', -} - -/** - * - * @param paths file paths - * @returns the path to a README.md, or undefined if none exist - */ -export const findReadmePath = (paths?: NewFileInfo[]) => { - return paths?.find((path) => /readme\.md$/i.test(path.relativePath)) -} diff --git a/packages/core/src/amazonqDoc/controllers/chat/controller.ts b/packages/core/src/amazonqDoc/controllers/chat/controller.ts deleted file mode 100644 index ab6045e75ce..00000000000 --- a/packages/core/src/amazonqDoc/controllers/chat/controller.ts +++ /dev/null @@ -1,715 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -import * as vscode from 'vscode' -import { EventEmitter } from 'vscode' - -import { - DocGenerationStep, - EditDocumentation, - FolderSelectorFollowUps, - Mode, - NewSessionFollowUps, - SynchronizeDocumentation, - CodeChangeFollowUps, - docScheme, - featureName, - findReadmePath, -} from '../../constants' -import { AuthUtil } from '../../../codewhisperer/util/authUtil' -import { getLogger } from '../../../shared/logger/logger' - -import { Session } from '../../session/session' -import { i18n } from '../../../shared/i18n-helper' -import path from 'path' -import { createSingleFileDialog } from '../../../shared/ui/common/openDialog' - -import { - MonthlyConversationLimitError, - SelectedFolderNotInWorkspaceFolderError, - WorkspaceFolderNotFoundError, - createUserFacingErrorMessage, - getMetricResult, -} from '../../../amazonqFeatureDev/errors' -import { BaseChatSessionStorage } from '../../../amazonq/commons/baseChatStorage' -import { DocMessenger } from '../../messenger' -import { AuthController } from '../../../amazonq/auth/controller' -import { openUrl } from '../../../shared/utilities/vsCodeUtils' -import { createAmazonQUri, openDeletedDiff, openDiff } from '../../../amazonq/commons/diff' -import { - getWorkspaceFoldersByPrefixes, - getWorkspaceRelativePath, - isMultiRootWorkspace, -} from '../../../shared/utilities/workspaceUtils' -import { getPathsFromZipFilePath, SvgFileExtension } from '../../../amazonq/util/files' -import { FollowUpTypes } from '../../../amazonq/commons/types' -import { DocGenerationTask, DocGenerationTasks } from '../docGenerationTask' -import { normalize } from '../../../shared/utilities/pathUtils' -import { DevPhase, MetricDataOperationName, MetricDataResult } from '../../types' - -export interface ChatControllerEventEmitters { - readonly processHumanChatMessage: EventEmitter - readonly followUpClicked: EventEmitter - readonly openDiff: EventEmitter - readonly stopResponse: EventEmitter - readonly tabOpened: EventEmitter - readonly tabClosed: EventEmitter - readonly processChatItemVotedMessage: EventEmitter - readonly processChatItemFeedbackMessage: EventEmitter - readonly authClicked: EventEmitter - readonly processResponseBodyLinkClick: EventEmitter - readonly insertCodeAtPositionClicked: EventEmitter - readonly fileClicked: EventEmitter - readonly formActionClicked: EventEmitter -} - -export class DocController { - private readonly scheme = docScheme - private readonly messenger: DocMessenger - private readonly sessionStorage: BaseChatSessionStorage - private authController: AuthController - private docGenerationTasks: DocGenerationTasks - - public constructor( - private readonly chatControllerMessageListeners: ChatControllerEventEmitters, - messenger: DocMessenger, - sessionStorage: BaseChatSessionStorage, - _onDidChangeAmazonQVisibility: vscode.Event - ) { - this.messenger = messenger - this.sessionStorage = sessionStorage - this.authController = new AuthController() - this.docGenerationTasks = new DocGenerationTasks() - - this.chatControllerMessageListeners.processHumanChatMessage.event((data) => { - this.processUserChatMessage(data).catch((e) => { - getLogger().error('processUserChatMessage failed: %s', (e as Error).message) - }) - }) - this.chatControllerMessageListeners.formActionClicked.event((data) => { - return this.formActionClicked(data) - }) - - this.initializeFollowUps() - - this.chatControllerMessageListeners.stopResponse.event((data) => { - return this.stopResponse(data) - }) - this.chatControllerMessageListeners.tabOpened.event((data) => { - return this.tabOpened(data) - }) - this.chatControllerMessageListeners.tabClosed.event((data) => { - this.tabClosed(data) - }) - this.chatControllerMessageListeners.authClicked.event((data) => { - this.authClicked(data) - }) - this.chatControllerMessageListeners.processResponseBodyLinkClick.event((data) => { - this.processLink(data) - }) - this.chatControllerMessageListeners.fileClicked.event(async (data) => { - return await this.fileClicked(data) - }) - this.chatControllerMessageListeners.openDiff.event(async (data) => { - return await this.openDiff(data) - }) - AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(() => { - this.sessionStorage.deleteAllSessions() - }) - } - - /** Prompts user to choose a folder in current workspace for README creation/update. - * After user chooses a folder, displays confirmation message to user with selected path. - * - */ - private async folderSelector(data: any) { - this.messenger.sendAnswer({ - type: 'answer', - tabID: data.tabID, - message: i18n('AWS.amazonq.doc.answer.chooseFolder'), - disableChatInput: true, - }) - - const uri = await createSingleFileDialog({ - canSelectFolders: true, - canSelectFiles: false, - }).prompt() - - const retryFollowUps = FolderSelectorFollowUps.filter( - (followUp) => followUp.type !== FollowUpTypes.ProceedFolderSelection - ) - - if (!(uri instanceof vscode.Uri)) { - this.messenger.sendAnswer({ - type: 'answer', - tabID: data.tabID, - message: i18n('AWS.amazonq.doc.error.noFolderSelected'), - followUps: retryFollowUps, - disableChatInput: true, - }) - // Check that selected folder is a subfolder of the current workspace - } else if (!vscode.workspace.getWorkspaceFolder(uri)) { - this.messenger.sendAnswer({ - type: 'answer', - tabID: data.tabID, - message: new SelectedFolderNotInWorkspaceFolderError().message, - followUps: retryFollowUps, - disableChatInput: true, - }) - } else { - let displayPath = '' - const relativePath = getWorkspaceRelativePath(uri.fsPath) - const docGenerationTask = this.docGenerationTasks.getTask(data.tabID) - if (relativePath) { - // Display path should always include workspace folder name - displayPath = path.join(relativePath.workspaceFolder.name, relativePath.relativePath) - // Only include workspace folder name in API call if multi-root workspace - docGenerationTask.folderPath = normalize( - isMultiRootWorkspace() ? displayPath : relativePath.relativePath - ) - - if (!relativePath.relativePath) { - docGenerationTask.folderLevel = 'ENTIRE_WORKSPACE' - } else { - docGenerationTask.folderLevel = 'SUB_FOLDER' - } - } - - this.messenger.sendFolderConfirmationMessage( - data.tabID, - docGenerationTask.mode === Mode.CREATE - ? i18n('AWS.amazonq.doc.answer.createReadme') - : i18n('AWS.amazonq.doc.answer.updateReadme'), - displayPath, - FolderSelectorFollowUps - ) - this.messenger.sendChatInputEnabled(data.tabID, false) - } - } - - private async openDiff(message: any) { - const tabId: string = message.tabID - const codeGenerationId: string = message.messageId - const zipFilePath: string = message.filePath - const session = await this.sessionStorage.getSession(tabId) - - const workspacePrefixMapping = getWorkspaceFoldersByPrefixes(session.config.workspaceFolders) - const pathInfos = getPathsFromZipFilePath(zipFilePath, workspacePrefixMapping, session.config.workspaceFolders) - - const extension = path.parse(message.filePath).ext - // Only open diffs on files, not directories - if (extension) { - if (message.deleted) { - const name = path.basename(pathInfos.relativePath) - await openDeletedDiff(pathInfos.absolutePath, name, tabId, this.scheme) - } else { - let uploadId = session.uploadId - if (session?.state?.uploadHistory && session.state.uploadHistory[codeGenerationId]) { - uploadId = session?.state?.uploadHistory[codeGenerationId].uploadId - } - const rightPath = path.join(uploadId, zipFilePath) - if (rightPath.toLowerCase().endsWith(SvgFileExtension)) { - const rightPathUri = createAmazonQUri(rightPath, tabId, this.scheme) - const infraDiagramContent = await vscode.workspace.openTextDocument(rightPathUri) - await vscode.window.showTextDocument(infraDiagramContent) - } else { - await openDiff(pathInfos.absolutePath, rightPath, tabId, this.scheme) - } - } - } - } - - private initializeFollowUps(): void { - this.chatControllerMessageListeners.followUpClicked.event(async (data) => { - const session: Session = await this.sessionStorage.getSession(data.tabID) - const docGenerationTask = this.docGenerationTasks.getTask(data.tabID) - - const workspaceFolders = vscode.workspace.workspaceFolders - if (workspaceFolders === undefined || workspaceFolders.length === 0) { - return - } - - const workspaceFolderName = vscode.workspace.workspaceFolders?.[0].name || '' - - const authState = await AuthUtil.instance.getChatAuthState() - - if (authState.amazonQ !== 'connected') { - await this.messenger.sendAuthNeededExceptionMessage(authState, data.tabID) - session.isAuthenticating = true - return - } - - const sendFolderConfirmationMessage = (message: string) => { - this.messenger.sendFolderConfirmationMessage( - data.tabID, - message, - workspaceFolderName, - FolderSelectorFollowUps - ) - } - - switch (data.followUp.type) { - case FollowUpTypes.Retry: - if (docGenerationTask.mode === Mode.EDIT) { - this.enableUserInput(data?.tabID) - } else { - await this.tabOpened(data) - } - break - case FollowUpTypes.NewTask: - this.messenger.sendAnswer({ - type: 'answer', - tabID: data?.tabID, - message: i18n('AWS.amazonq.featureDev.answer.newTaskChanges'), - disableChatInput: true, - }) - return this.newTask(data) - case FollowUpTypes.CloseSession: - return this.closeSession(data) - case FollowUpTypes.CreateDocumentation: - docGenerationTask.interactionType = 'GENERATE_README' - docGenerationTask.mode = Mode.CREATE - sendFolderConfirmationMessage(i18n('AWS.amazonq.doc.answer.createReadme')) - break - case FollowUpTypes.ChooseFolder: - await this.folderSelector(data) - break - case FollowUpTypes.SynchronizeDocumentation: - docGenerationTask.mode = Mode.SYNC - sendFolderConfirmationMessage(i18n('AWS.amazonq.doc.answer.updateReadme')) - break - case FollowUpTypes.UpdateDocumentation: - docGenerationTask.interactionType = 'UPDATE_README' - this.messenger.sendAnswer({ - type: 'answer', - tabID: data?.tabID, - followUps: [SynchronizeDocumentation, EditDocumentation], - disableChatInput: true, - }) - break - case FollowUpTypes.EditDocumentation: - docGenerationTask.interactionType = 'EDIT_README' - docGenerationTask.mode = Mode.EDIT - sendFolderConfirmationMessage(i18n('AWS.amazonq.doc.answer.updateReadme')) - break - case FollowUpTypes.MakeChanges: - docGenerationTask.mode = Mode.EDIT - this.enableUserInput(data.tabID) - break - case FollowUpTypes.AcceptChanges: - docGenerationTask.userDecision = 'ACCEPT' - await this.sendDocAcceptanceEvent(data) - await this.insertCode(data) - return - case FollowUpTypes.RejectChanges: - docGenerationTask.userDecision = 'REJECT' - await this.sendDocAcceptanceEvent(data) - this.messenger.sendAnswer({ - type: 'answer', - tabID: data?.tabID, - disableChatInput: true, - message: 'Your changes have been discarded.', - followUps: NewSessionFollowUps, - }) - break - case FollowUpTypes.ProceedFolderSelection: - // If a user did not change the folder in a multi-root workspace, default to the first workspace folder - if (docGenerationTask.folderPath === '' && isMultiRootWorkspace()) { - docGenerationTask.folderPath = workspaceFolderName - } - if (docGenerationTask.mode === Mode.EDIT) { - this.enableUserInput(data.tabID) - } else { - await this.generateDocumentation( - { - ...data, - message: - docGenerationTask.mode === Mode.CREATE - ? 'Create documentation for a specific folder' - : 'Sync documentation', - }, - session, - docGenerationTask - ) - } - break - case FollowUpTypes.CancelFolderSelection: - docGenerationTask.reset() - return this.tabOpened(data) - } - }) - } - - private enableUserInput(tabID: string) { - this.messenger.sendAnswer({ - type: 'answer', - tabID: tabID, - message: i18n('AWS.amazonq.doc.answer.editReadme'), - }) - this.messenger.sendUpdatePlaceholder(tabID, i18n('AWS.amazonq.doc.placeholder.editReadme')) - this.messenger.sendChatInputEnabled(tabID, true) - } - - private async fileClicked(message: any) { - const tabId: string = message.tabID - const messageId = message.messageId - const filePathToUpdate: string = message.filePath - - const session = await this.sessionStorage.getSession(tabId) - const filePathIndex = (session.state.filePaths ?? []).findIndex((obj) => obj.relativePath === filePathToUpdate) - if (filePathIndex !== -1 && session.state.filePaths) { - session.state.filePaths[filePathIndex].rejected = !session.state.filePaths[filePathIndex].rejected - } - const deletedFilePathIndex = (session.state.deletedFiles ?? []).findIndex( - (obj) => obj.relativePath === filePathToUpdate - ) - if (deletedFilePathIndex !== -1 && session.state.deletedFiles) { - session.state.deletedFiles[deletedFilePathIndex].rejected = - !session.state.deletedFiles[deletedFilePathIndex].rejected - } - - await session.updateFilesPaths( - tabId, - session.state.filePaths ?? [], - session.state.deletedFiles ?? [], - messageId, - true - ) - } - - private async formActionClicked(message: any) { - switch (message.action) { - case 'cancel-doc-generation': - // eslint-disable-next-line unicorn/no-null - await this.stopResponse(message) - - break - } - } - - private async newTask(message: any) { - // Old session for the tab is ending, delete it so we can create a new one for the message id - - this.docGenerationTasks.deleteTask(message.tabID) - this.sessionStorage.deleteSession(message.tabID) - - // Re-run the opening flow, where we check auth + create a session - await this.tabOpened(message) - } - - private async closeSession(message: any) { - this.messenger.sendAnswer({ - type: 'answer', - tabID: message.tabID, - message: i18n('AWS.amazonq.featureDev.answer.sessionClosed'), - disableChatInput: true, - }) - this.messenger.sendUpdatePlaceholder(message.tabID, i18n('AWS.amazonq.featureDev.placeholder.sessionClosed')) - this.messenger.sendChatInputEnabled(message.tabID, false) - this.docGenerationTasks.deleteTask(message.tabID) - } - - private processErrorChatMessage = ( - err: any, - message: any, - session: Session | undefined, - docGenerationTask: DocGenerationTask - ) => { - const errorMessage = createUserFacingErrorMessage(`${err.cause?.message ?? err.message}`) - // eslint-disable-next-line unicorn/no-null - this.messenger.sendUpdatePromptProgress(message.tabID, null) - if (err.constructor.name === MonthlyConversationLimitError.name) { - this.messenger.sendMonthlyLimitError(message.tabID) - } else { - const enableUserInput = docGenerationTask.mode === Mode.EDIT && err.remainingIterations > 0 - - this.messenger.sendErrorMessage( - errorMessage, - message.tabID, - 0, - session?.conversationIdUnsafe, - false, - enableUserInput - ) - } - } - - private async generateDocumentation(message: any, session: any, docGenerationTask: DocGenerationTask) { - try { - await this.onDocsGeneration(session, message.message, message.tabID, docGenerationTask) - } catch (err: any) { - this.processErrorChatMessage(err, message, session, docGenerationTask) - } - } - - private async processUserChatMessage(message: any) { - if (message.message === undefined) { - this.messenger.sendErrorMessage('chatMessage should be set', message.tabID, 0, undefined) - return - } - - /** - * Don't attempt to process any chat messages when a workspace folder is not set. - * When the tab is first opened we will throw an error and lock the chat if the workspace - * folder is not found - */ - const workspaceFolders = vscode.workspace.workspaceFolders - if (workspaceFolders === undefined || workspaceFolders.length === 0) { - return - } - - const session: Session = await this.sessionStorage.getSession(message.tabID) - const docGenerationTask = this.docGenerationTasks.getTask(message.tabID) - - try { - getLogger().debug(`${featureName}: Processing message: ${message.message}`) - - const authState = await AuthUtil.instance.getChatAuthState() - if (authState.amazonQ !== 'connected') { - await this.messenger.sendAuthNeededExceptionMessage(authState, message.tabID) - session.isAuthenticating = true - return - } - - await this.generateDocumentation(message, session, docGenerationTask) - } catch (err: any) { - this.processErrorChatMessage(err, message, session, docGenerationTask) - } - } - - private async stopResponse(message: any) { - this.messenger.sendAnswer({ - message: i18n('AWS.amazonq.featureDev.pillText.stoppingCodeGeneration'), - type: 'answer-part', - tabID: message.tabID, - }) - // eslint-disable-next-line unicorn/no-null - this.messenger.sendUpdatePromptProgress(message.tabID, null) - this.messenger.sendChatInputEnabled(message.tabID, false) - - const session = await this.sessionStorage.getSession(message.tabID) - session.state.tokenSource?.cancel() - } - - private async tabOpened(message: any) { - let session: Session | undefined - try { - session = await this.sessionStorage.getSession(message.tabID) - const docGenerationTask = this.docGenerationTasks.getTask(message.tabID) - getLogger().debug(`${featureName}: Session created with id: ${session.tabID}`) - docGenerationTask.folderPath = '' - docGenerationTask.mode = Mode.NONE - - const authState = await AuthUtil.instance.getChatAuthState() - if (authState.amazonQ !== 'connected') { - void this.messenger.sendAuthNeededExceptionMessage(authState, message.tabID) - session.isAuthenticating = true - return - } - docGenerationTask.numberOfNavigations += 1 - this.messenger.sendAnswer({ - type: 'answer', - tabID: message.tabID, - followUps: [ - { - pillText: 'Create a README', - prompt: 'Create a README', - type: 'CreateDocumentation', - }, - { - pillText: 'Update an existing README', - prompt: 'Update an existing README', - type: 'UpdateDocumentation', - }, - ], - disableChatInput: true, - }) - this.messenger.sendUpdatePlaceholder(message.tabID, i18n('AWS.amazonq.doc.pillText.selectOption')) - } catch (err: any) { - if (err instanceof WorkspaceFolderNotFoundError) { - this.messenger.sendAnswer({ - type: 'answer', - tabID: message.tabID, - message: err.message, - disableChatInput: true, - }) - } else { - this.messenger.sendErrorMessage( - createUserFacingErrorMessage(err.message), - message.tabID, - 0, - session?.conversationIdUnsafe - ) - } - } - } - - private async openMarkdownPreview(readmePath: vscode.Uri) { - await vscode.commands.executeCommand('vscode.open', readmePath) - await vscode.commands.executeCommand('markdown.showPreview') - } - - private async onDocsGeneration( - session: Session, - message: string, - tabID: string, - docGenerationTask: DocGenerationTask - ) { - this.messenger.sendDocProgress(tabID, DocGenerationStep.UPLOAD_TO_S3, 0, docGenerationTask.mode) - - await session.preloader(message) - - try { - await session.sendDocMetricData(MetricDataOperationName.StartDocGeneration, MetricDataResult.Success) - await session.send(message, docGenerationTask.mode, docGenerationTask.folderPath) - const filePaths = session.state.filePaths ?? [] - const deletedFiles = session.state.deletedFiles ?? [] - - // Only add the follow up accept/deny buttons when the tab hasn't been closed/request hasn't been cancelled - if (session?.state.tokenSource?.token.isCancellationRequested) { - return - } - - if (filePaths.length === 0 && deletedFiles.length === 0) { - this.messenger.sendAnswer({ - message: i18n('AWS.amazonq.featureDev.pillText.unableGenerateChanges'), - type: 'answer', - tabID: tabID, - canBeVoted: true, - disableChatInput: true, - }) - - return - } - - this.messenger.sendCodeResult( - filePaths, - deletedFiles, - session.state.references ?? [], - tabID, - session.uploadId, - session.state.codeGenerationId ?? '' - ) - - // Automatically open the README diff - const readmePath = findReadmePath(session.state.filePaths) - if (readmePath) { - await this.openDiff({ tabID, filePath: readmePath.zipFilePath }) - } - - const remainingIterations = session.state.codeGenerationRemainingIterationCount - const totalIterations = session.state.codeGenerationTotalIterationCount - - if (remainingIterations !== undefined && totalIterations !== undefined) { - this.messenger.sendAnswer({ - type: 'answer', - tabID: tabID, - message: `${docGenerationTask.mode === Mode.CREATE ? i18n('AWS.amazonq.doc.answer.readmeCreated') : i18n('AWS.amazonq.doc.answer.readmeUpdated')} ${remainingIterations > 0 ? i18n('AWS.amazonq.doc.answer.codeResult') : i18n('AWS.amazonq.doc.answer.acceptOrReject')}`, - disableChatInput: true, - }) - - this.messenger.sendAnswer({ - message: undefined, - type: 'system-prompt', - disableChatInput: true, - followUps: - remainingIterations > 0 - ? CodeChangeFollowUps - : CodeChangeFollowUps.filter((followUp) => followUp.type !== FollowUpTypes.MakeChanges), - tabID: tabID, - }) - } - if (session?.state.phase === DevPhase.CODEGEN) { - const docGenerationTask = this.docGenerationTasks.getTask(tabID) - const { totalGeneratedChars, totalGeneratedLines, totalGeneratedFiles } = - await session.countGeneratedContent(docGenerationTask.interactionType) - docGenerationTask.conversationId = session.conversationId - docGenerationTask.numberOfGeneratedChars = totalGeneratedChars - docGenerationTask.numberOfGeneratedLines = totalGeneratedLines - docGenerationTask.numberOfGeneratedFiles = totalGeneratedFiles - const docGenerationEvent = docGenerationTask.docGenerationEventBase() - - await session.sendDocTelemetryEvent(docGenerationEvent, 'generation') - } - } catch (err: any) { - getLogger().error(`${featureName}: Error during doc generation: ${err}`) - await session.sendDocMetricData(MetricDataOperationName.EndDocGeneration, getMetricResult(err)) - throw err - } finally { - if (session?.state?.tokenSource?.token.isCancellationRequested) { - await this.newTask({ tabID }) - } else { - this.messenger.sendUpdatePlaceholder(tabID, i18n('AWS.amazonq.doc.pillText.selectOption')) - - this.messenger.sendChatInputEnabled(tabID, false) - } - } - await session.sendDocMetricData(MetricDataOperationName.EndDocGeneration, MetricDataResult.Success) - } - - private authClicked(message: any) { - this.authController.handleAuth(message.authType) - - this.messenger.sendAnswer({ - type: 'answer', - tabID: message.tabID, - message: 'Follow instructions to re-authenticate ...', - disableChatInput: true, - }) - } - - private tabClosed(message: any) { - this.sessionStorage.deleteSession(message.tabID) - this.docGenerationTasks.deleteTask(message.tabID) - } - - private async insertCode(message: any) { - let session - try { - session = await this.sessionStorage.getSession(message.tabID) - - await session.insertChanges() - - const readmePath = findReadmePath(session.state.filePaths) - if (readmePath) { - await this.openMarkdownPreview( - vscode.Uri.file(path.join(readmePath.workspaceFolder.uri.fsPath, readmePath.relativePath)) - ) - } - - this.messenger.sendAnswer({ - type: 'answer', - disableChatInput: true, - tabID: message.tabID, - followUps: NewSessionFollowUps, - }) - - this.messenger.sendUpdatePlaceholder(message.tabID, i18n('AWS.amazonq.doc.pillText.selectOption')) - } catch (err: any) { - this.messenger.sendErrorMessage( - createUserFacingErrorMessage(`Failed to insert code changes: ${err.message}`), - message.tabID, - 0, - session?.conversationIdUnsafe - ) - } - } - private async sendDocAcceptanceEvent(message: any) { - const session = await this.sessionStorage.getSession(message.tabID) - const docGenerationTask = this.docGenerationTasks.getTask(message.tabID) - docGenerationTask.conversationId = session.conversationId - const { totalAddedChars, totalAddedLines, totalAddedFiles } = await session.countAddedContent( - docGenerationTask.interactionType - ) - docGenerationTask.numberOfAddedChars = totalAddedChars - docGenerationTask.numberOfAddedLines = totalAddedLines - docGenerationTask.numberOfAddedFiles = totalAddedFiles - const docAcceptanceEvent = docGenerationTask.docAcceptanceEventBase() - - await session.sendDocTelemetryEvent(docAcceptanceEvent, 'acceptance') - } - private processLink(message: any) { - void openUrl(vscode.Uri.parse(message.link)) - } -} diff --git a/packages/core/src/amazonqDoc/controllers/docGenerationTask.ts b/packages/core/src/amazonqDoc/controllers/docGenerationTask.ts deleted file mode 100644 index fe5dc25981c..00000000000 --- a/packages/core/src/amazonqDoc/controllers/docGenerationTask.ts +++ /dev/null @@ -1,100 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -import { - DocFolderLevel, - DocInteractionType, - DocUserDecision, - DocV2AcceptanceEvent, - DocV2GenerationEvent, -} from '../../codewhisperer/client/codewhispereruserclient' -import { getLogger } from '../../shared/logger/logger' -import { Mode } from '../constants' - -export class DocGenerationTasks { - private tasks: Map = new Map() - - public getTask(tabId: string): DocGenerationTask { - if (!this.tasks.has(tabId)) { - this.tasks.set(tabId, new DocGenerationTask()) - } - return this.tasks.get(tabId)! - } - - public deleteTask(tabId: string): void { - this.tasks.delete(tabId) - } -} - -export class DocGenerationTask { - public mode: Mode = Mode.NONE - public folderPath = '' - // Telemetry fields - public conversationId?: string - public numberOfAddedChars?: number - public numberOfAddedLines?: number - public numberOfAddedFiles?: number - public numberOfGeneratedChars?: number - public numberOfGeneratedLines?: number - public numberOfGeneratedFiles?: number - public userDecision?: DocUserDecision - public interactionType?: DocInteractionType - public numberOfNavigations = 0 - public folderLevel: DocFolderLevel = 'ENTIRE_WORKSPACE' - - public docGenerationEventBase() { - const undefinedProps = Object.entries(this) - .filter(([key, value]) => value === undefined) - .map(([key]) => key) - - if (undefinedProps.length > 0) { - getLogger().debug(`DocV2GenerationEvent has undefined properties: ${undefinedProps.join(', ')}`) - } - const event: DocV2GenerationEvent = { - conversationId: this.conversationId ?? '', - numberOfGeneratedChars: this.numberOfGeneratedChars ?? 0, - numberOfGeneratedLines: this.numberOfGeneratedLines ?? 0, - numberOfGeneratedFiles: this.numberOfGeneratedFiles ?? 0, - interactionType: this.interactionType, - numberOfNavigations: this.numberOfNavigations, - folderLevel: this.folderLevel, - } - return event - } - - public docAcceptanceEventBase() { - const undefinedProps = Object.entries(this) - .filter(([key, value]) => value === undefined) - .map(([key]) => key) - - if (undefinedProps.length > 0) { - getLogger().debug(`DocV2AcceptanceEvent has undefined properties: ${undefinedProps.join(', ')}`) - } - const event: DocV2AcceptanceEvent = { - conversationId: this.conversationId ?? '', - numberOfAddedChars: this.numberOfAddedChars ?? 0, - numberOfAddedLines: this.numberOfAddedLines ?? 0, - numberOfAddedFiles: this.numberOfAddedFiles ?? 0, - userDecision: this.userDecision ?? 'ACCEPTED', - interactionType: this.interactionType ?? 'GENERATE_README', - numberOfNavigations: this.numberOfNavigations ?? 0, - folderLevel: this.folderLevel, - } - return event - } - - public reset() { - this.conversationId = undefined - this.numberOfAddedChars = undefined - this.numberOfAddedLines = undefined - this.numberOfAddedFiles = undefined - this.numberOfGeneratedChars = undefined - this.numberOfGeneratedLines = undefined - this.numberOfGeneratedFiles = undefined - this.userDecision = undefined - this.interactionType = undefined - this.numberOfNavigations = 0 - this.folderLevel = 'ENTIRE_WORKSPACE' - } -} diff --git a/packages/core/src/amazonqDoc/errors.ts b/packages/core/src/amazonqDoc/errors.ts deleted file mode 100644 index fb1ffa033c2..00000000000 --- a/packages/core/src/amazonqDoc/errors.ts +++ /dev/null @@ -1,63 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { ClientError, ContentLengthError as CommonContentLengthError } from '../shared/errors' -import { i18n } from '../shared/i18n-helper' - -export class DocClientError extends ClientError { - remainingIterations?: number - constructor(message: string, code: string, remainingIterations?: number) { - super(message, { code }) - this.remainingIterations = remainingIterations - } -} - -export class ReadmeTooLargeError extends DocClientError { - constructor() { - super(i18n('AWS.amazonq.doc.error.readmeTooLarge'), ReadmeTooLargeError.name) - } -} - -export class ReadmeUpdateTooLargeError extends DocClientError { - constructor(remainingIterations: number) { - super(i18n('AWS.amazonq.doc.error.readmeUpdateTooLarge'), ReadmeUpdateTooLargeError.name, remainingIterations) - } -} - -export class WorkspaceEmptyError extends DocClientError { - constructor() { - super(i18n('AWS.amazonq.doc.error.workspaceEmpty'), WorkspaceEmptyError.name) - } -} - -export class NoChangeRequiredException extends DocClientError { - constructor() { - super(i18n('AWS.amazonq.doc.error.noChangeRequiredException'), NoChangeRequiredException.name) - } -} - -export class PromptRefusalException extends DocClientError { - constructor(remainingIterations: number) { - super(i18n('AWS.amazonq.doc.error.promptRefusal'), PromptRefusalException.name, remainingIterations) - } -} - -export class ContentLengthError extends CommonContentLengthError { - constructor() { - super(i18n('AWS.amazonq.doc.error.contentLengthError'), { code: ContentLengthError.name }) - } -} - -export class PromptTooVagueError extends DocClientError { - constructor(remainingIterations: number) { - super(i18n('AWS.amazonq.doc.error.promptTooVague'), PromptTooVagueError.name, remainingIterations) - } -} - -export class PromptUnrelatedError extends DocClientError { - constructor(remainingIterations: number) { - super(i18n('AWS.amazonq.doc.error.promptUnrelated'), PromptUnrelatedError.name, remainingIterations) - } -} diff --git a/packages/core/src/amazonqDoc/index.ts b/packages/core/src/amazonqDoc/index.ts deleted file mode 100644 index 7ba22e4b351..00000000000 --- a/packages/core/src/amazonqDoc/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -export * from './types' -export * from './session/sessionState' -export * from './constants' -export { Session } from './session/session' -export { ChatControllerEventEmitters, DocController } from './controllers/chat/controller' diff --git a/packages/core/src/amazonqDoc/messenger.ts b/packages/core/src/amazonqDoc/messenger.ts deleted file mode 100644 index 3c6abfdf15f..00000000000 --- a/packages/core/src/amazonqDoc/messenger.ts +++ /dev/null @@ -1,65 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -import { Messenger } from '../amazonq/commons/connector/baseMessenger' -import { AppToWebViewMessageDispatcher } from '../amazonq/commons/connector/connectorMessages' -import { messageWithConversationId } from '../amazonqFeatureDev/userFacingText' -import { i18n } from '../shared/i18n-helper' -import { docGenerationProgressMessage, DocGenerationStep, Mode, NewSessionFollowUps } from './constants' -import { inProgress } from './types' - -export class DocMessenger extends Messenger { - public constructor(dispatcher: AppToWebViewMessageDispatcher, sender: string) { - super(dispatcher, sender) - } - - /** Sends a message in the chat and displays a prompt input progress bar to communicate the doc generation progress. - * The text in the progress bar matches the current step shown in the message. - * - */ - public sendDocProgress(tabID: string, step: DocGenerationStep, progress: number, mode: Mode) { - // Hide prompt input progress bar once all steps are completed - if (step > DocGenerationStep.GENERATING_ARTIFACTS) { - // eslint-disable-next-line unicorn/no-null - this.sendUpdatePromptProgress(tabID, null) - } else { - const progressText = - step === DocGenerationStep.UPLOAD_TO_S3 - ? `${i18n('AWS.amazonq.doc.answer.scanning')}...` - : step === DocGenerationStep.SUMMARIZING_FILES - ? `${i18n('AWS.amazonq.doc.answer.summarizing')}...` - : `${i18n('AWS.amazonq.doc.answer.generating')}...` - this.sendUpdatePromptProgress(tabID, inProgress(progress, progressText)) - } - - // The first step is answer-stream type, subequent updates are answer-part - this.sendAnswer({ - type: step === DocGenerationStep.UPLOAD_TO_S3 ? 'answer-stream' : 'answer-part', - tabID: tabID, - disableChatInput: true, - message: docGenerationProgressMessage(step, mode), - }) - } - - public override sendErrorMessage( - errorMessage: string, - tabID: string, - _retries: number, - conversationId?: string, - _showDefaultMessage?: boolean, - enableUserInput?: boolean - ) { - if (enableUserInput) { - this.sendUpdatePlaceholder(tabID, i18n('AWS.amazonq.doc.placeholder.editReadme')) - this.sendChatInputEnabled(tabID, true) - } - this.sendAnswer({ - type: 'answer', - tabID: tabID, - message: errorMessage + messageWithConversationId(conversationId), - followUps: enableUserInput ? [] : NewSessionFollowUps, - disableChatInput: !enableUserInput, - }) - } -} diff --git a/packages/core/src/amazonqDoc/session/session.ts b/packages/core/src/amazonqDoc/session/session.ts deleted file mode 100644 index e3eb29d6d32..00000000000 --- a/packages/core/src/amazonqDoc/session/session.ts +++ /dev/null @@ -1,371 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { docScheme, featureName, Mode } from '../constants' -import { DeletedFileInfo, Interaction, NewFileInfo, SessionState, SessionStateConfig } from '../types' -import { DocPrepareCodeGenState } from './sessionState' -import { telemetry } from '../../shared/telemetry/telemetry' -import { AuthUtil } from '../../codewhisperer/util/authUtil' -import { SessionConfig } from '../../amazonq/commons/session/sessionConfigFactory' -import path from 'path' -import { FeatureDevClient } from '../../amazonqFeatureDev/client/featureDev' -import { TelemetryHelper } from '../../amazonq/util/telemetryHelper' -import { ConversationNotStartedState } from '../../amazonqFeatureDev/session/sessionState' -import { logWithConversationId } from '../../amazonqFeatureDev/userFacingText' -import { ConversationIdNotFoundError, IllegalStateError } from '../../amazonqFeatureDev/errors' -import { referenceLogText } from '../../amazonq/commons/model' -import { - DocInteractionType, - DocV2AcceptanceEvent, - DocV2GenerationEvent, - SendTelemetryEventRequest, - MetricData, -} from '../../codewhisperer/client/codewhispereruserclient' -import { getClientId, getOperatingSystem, getOptOutPreference } from '../../shared/telemetry/util' -import { DocMessenger } from '../messenger' -import { computeDiff } from '../../amazonq/commons/diff' -import { ReferenceLogViewProvider } from '../../codewhisperer/service/referenceLogViewProvider' -import fs from '../../shared/fs/fs' -import globals from '../../shared/extensionGlobals' -import { extensionVersion } from '../../shared/vscode/env' -import { getLogger } from '../../shared/logger/logger' -import { ContentLengthError as CommonContentLengthError } from '../../shared/errors' -import { ContentLengthError } from '../errors' - -export class Session { - private _state?: SessionState | Omit - private task: string = '' - private proxyClient: FeatureDevClient - private _conversationId?: string - private preloaderFinished = false - private _latestMessage: string = '' - private _telemetry: TelemetryHelper - - // Used to keep track of whether or not the current session is currently authenticating/needs authenticating - public isAuthenticating: boolean - private _reportedDocChanges: { [key: string]: string } = {} - - constructor( - public readonly config: SessionConfig, - private messenger: DocMessenger, - public readonly tabID: string, - initialState: Omit = new ConversationNotStartedState(tabID), - proxyClient: FeatureDevClient = new FeatureDevClient() - ) { - this._state = initialState - this.proxyClient = proxyClient - - this._telemetry = new TelemetryHelper() - this.isAuthenticating = false - } - - /** - * Preload any events that have to run before a chat message can be sent - */ - async preloader(msg: string) { - if (!this.preloaderFinished) { - await this.setupConversation(msg) - this.preloaderFinished = true - } - } - - get state() { - if (!this._state) { - throw new IllegalStateError("State should be initialized before it's read") - } - return this._state - } - - /** - * setupConversation - * - * Starts a conversation with the backend and uploads the repo for the LLMs to be able to use it. - */ - private async setupConversation(msg: string) { - // Store the initial message when setting up the conversation so that if it fails we can retry with this message - this._latestMessage = msg - - await telemetry.amazonq_startConversationInvoke.run(async (span) => { - this._conversationId = await this.proxyClient.createConversation() - getLogger().info(logWithConversationId(this.conversationId)) - - span.record({ amazonqConversationId: this._conversationId, credentialStartUrl: AuthUtil.instance.startUrl }) - }) - - this._state = new DocPrepareCodeGenState( - { - ...this.getSessionStateConfig(), - conversationId: this.conversationId, - uploadId: '', - currentCodeGenerationId: undefined, - }, - [], - [], - [], - this.tabID, - 0 - ) - } - - private getSessionStateConfig(): Omit { - return { - workspaceRoots: this.config.workspaceRoots, - workspaceFolders: this.config.workspaceFolders, - proxyClient: this.proxyClient, - conversationId: this.conversationId, - } - } - - async send(msg: string, mode: Mode, folderPath?: string): Promise { - // When the task/"thing to do" hasn't been set yet, we want it to be the incoming message - if (this.task === '' && msg) { - this.task = msg - } - - this._latestMessage = msg - - return this.nextInteraction(msg, mode, folderPath) - } - private async nextInteraction(msg: string, mode: Mode, folderPath?: string) { - try { - const resp = await this.state.interact({ - task: this.task, - msg, - fs: this.config.fs, - mode: mode, - folderPath: folderPath, - messenger: this.messenger, - telemetry: this.telemetry, - tokenSource: this.state.tokenSource, - uploadHistory: this.state.uploadHistory, - }) - - if (resp.nextState) { - if (!this.state?.tokenSource?.token.isCancellationRequested) { - this.state?.tokenSource?.cancel() - } - - // Move to the next state - this._state = resp.nextState - } - - return resp.interaction - } catch (e) { - if (e instanceof CommonContentLengthError) { - getLogger().debug(`Content length validation failed: ${e.message}`) - throw new ContentLengthError() - } - throw e - } - } - - public async updateFilesPaths( - tabID: string, - filePaths: NewFileInfo[], - deletedFiles: DeletedFileInfo[], - messageId: string, - disableFileActions: boolean - ) { - this.messenger.updateFileComponent(tabID, filePaths, deletedFiles, messageId, disableFileActions) - } - - public async insertChanges() { - for (const filePath of this.state.filePaths?.filter((i) => !i.rejected) ?? []) { - const absolutePath = path.join(filePath.workspaceFolder.uri.fsPath, filePath.relativePath) - - const uri = filePath.virtualMemoryUri - const content = await this.config.fs.readFile(uri) - const decodedContent = new TextDecoder().decode(content) - - await fs.mkdir(path.dirname(absolutePath)) - await fs.writeFile(absolutePath, decodedContent) - } - - for (const filePath of this.state.deletedFiles?.filter((i) => !i.rejected) ?? []) { - const absolutePath = path.join(filePath.workspaceFolder.uri.fsPath, filePath.relativePath) - await fs.delete(absolutePath) - } - - for (const ref of this.state.references ?? []) { - ReferenceLogViewProvider.instance.addReferenceLog(referenceLogText(ref)) - } - } - - private getFromReportedChanges(filepath: NewFileInfo) { - const key = `${filepath.workspaceFolder.uri.fsPath}/${filepath.relativePath}` - return this._reportedDocChanges[key] - } - - private addToReportedChanges(filepath: NewFileInfo) { - const key = `${filepath.workspaceFolder.uri.fsPath}/${filepath.relativePath}` - this._reportedDocChanges[key] = filepath.fileContent - } - - public async countGeneratedContent(interactionType?: DocInteractionType) { - let totalGeneratedChars = 0 - let totalGeneratedLines = 0 - let totalGeneratedFiles = 0 - const filePaths = this.state.filePaths ?? [] - - for (const filePath of filePaths) { - const reportedDocChange = this.getFromReportedChanges(filePath) - if (interactionType === 'GENERATE_README') { - if (reportedDocChange) { - const { charsAdded, linesAdded } = await this.computeFilePathDiff(filePath, reportedDocChange) - totalGeneratedChars += charsAdded - totalGeneratedLines += linesAdded - } else { - // If no changes are reported, this is the initial README generation and no comparison with existing files is needed - const fileContent = filePath.fileContent - totalGeneratedChars += fileContent.length - totalGeneratedLines += fileContent.split('\n').length - } - } else { - const { charsAdded, linesAdded } = await this.computeFilePathDiff(filePath, reportedDocChange) - totalGeneratedChars += charsAdded - totalGeneratedLines += linesAdded - } - this.addToReportedChanges(filePath) - totalGeneratedFiles += 1 - } - return { - totalGeneratedChars, - totalGeneratedLines, - totalGeneratedFiles, - } - } - - public async countAddedContent(interactionType?: DocInteractionType) { - let totalAddedChars = 0 - let totalAddedLines = 0 - let totalAddedFiles = 0 - const newFilePaths = - this.state.filePaths?.filter((filePath) => !filePath.rejected && !filePath.changeApplied) ?? [] - - for (const filePath of newFilePaths) { - if (interactionType === 'GENERATE_README') { - const fileContent = filePath.fileContent - totalAddedChars += fileContent.length - totalAddedLines += fileContent.split('\n').length - } else { - const { charsAdded, linesAdded } = await this.computeFilePathDiff(filePath) - totalAddedChars += charsAdded - totalAddedLines += linesAdded - } - totalAddedFiles += 1 - } - return { - totalAddedChars, - totalAddedLines, - totalAddedFiles, - } - } - - public async computeFilePathDiff(filePath: NewFileInfo, reportedChanges?: string) { - const leftPath = `${filePath.workspaceFolder.uri.fsPath}/${filePath.relativePath}` - const rightPath = filePath.virtualMemoryUri.path - const diff = await computeDiff(leftPath, rightPath, this.tabID, docScheme, reportedChanges) - return { leftPath, rightPath, ...diff } - } - - public async sendDocMetricData(operationName: string, result: string) { - const metricData = { - metricName: 'Operation', - metricValue: 1, - timestamp: new Date(), - product: 'DocGeneration', - dimensions: [ - { - name: 'operationName', - value: operationName, - }, - { - name: 'result', - value: result, - }, - ], - } - await this.sendDocTelemetryEvent(metricData, 'metric') - } - - public async sendDocTelemetryEvent( - telemetryEvent: DocV2GenerationEvent | DocV2AcceptanceEvent | MetricData, - eventType: 'generation' | 'acceptance' | 'metric' - ) { - const client = await this.proxyClient.getClient() - const telemetryEventKey = { - generation: 'docV2GenerationEvent', - acceptance: 'docV2AcceptanceEvent', - metric: 'metricData', - }[eventType] - try { - const params: SendTelemetryEventRequest = { - telemetryEvent: { - [telemetryEventKey]: telemetryEvent, - }, - optOutPreference: getOptOutPreference(), - userContext: { - ideCategory: 'VSCODE', - operatingSystem: getOperatingSystem(), - product: 'DocGeneration', // Should be the same as in JetBrains - clientId: getClientId(globals.globalState), - ideVersion: extensionVersion, - }, - profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, - } - - const response = await client.sendTelemetryEvent(params).promise() - if (eventType === 'metric') { - getLogger().debug( - `${featureName}: successfully sent metricData: RequestId: ${response.$response.requestId}` - ) - } else { - getLogger().debug( - `${featureName}: successfully sent docV2${eventType === 'generation' ? 'GenerationEvent' : 'AcceptanceEvent'}: ` + - `ConversationId: ${(telemetryEvent as DocV2GenerationEvent | DocV2AcceptanceEvent).conversationId} ` + - `RequestId: ${response.$response.requestId}` - ) - } - } catch (e) { - const error = e as Error - const eventTypeString = eventType === 'metric' ? 'metricData' : `doc ${eventType}` - getLogger().error( - `${featureName}: failed to send ${eventTypeString} telemetry: ${error.name}: ${error.message} ` + - `RequestId: ${(e as any).$response?.requestId}` - ) - } - } - - get currentCodeGenerationId() { - return this.state.currentCodeGenerationId - } - - get uploadId() { - if (!('uploadId' in this.state)) { - throw new IllegalStateError("UploadId has to be initialized before it's read") - } - return this.state.uploadId - } - - get conversationId() { - if (!this._conversationId) { - throw new ConversationIdNotFoundError() - } - return this._conversationId - } - - // Used for cases where it is not needed to have conversationId - get conversationIdUnsafe() { - return this._conversationId - } - - get latestMessage() { - return this._latestMessage - } - - get telemetry() { - return this._telemetry - } -} diff --git a/packages/core/src/amazonqDoc/session/sessionState.ts b/packages/core/src/amazonqDoc/session/sessionState.ts deleted file mode 100644 index e95bc48f772..00000000000 --- a/packages/core/src/amazonqDoc/session/sessionState.ts +++ /dev/null @@ -1,165 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { DocGenerationStep, docScheme, getFileSummaryPercentage, Mode } from '../constants' - -import { i18n } from '../../shared/i18n-helper' - -import { CurrentWsFolders, NewFileInfo, SessionState, SessionStateAction, SessionStateConfig } from '../types' -import { - ContentLengthError, - NoChangeRequiredException, - PromptRefusalException, - PromptTooVagueError, - PromptUnrelatedError, - ReadmeTooLargeError, - ReadmeUpdateTooLargeError, - WorkspaceEmptyError, -} from '../errors' -import { ApiClientError, ApiServiceError } from '../../amazonqFeatureDev/errors' -import { DocMessenger } from '../messenger' -import { BaseCodeGenState, BasePrepareCodeGenState, CreateNextStateParams } from '../../amazonq/session/sessionState' -import { Intent } from '../../amazonq/commons/types' -import { AmazonqCreateUpload, Span } from '../../shared/telemetry/telemetry' -import { prepareRepoData, PrepareRepoDataOptions } from '../../amazonq/util/files' -import { LlmError } from '../../amazonq/errors' - -export class DocCodeGenState extends BaseCodeGenState { - protected handleProgress(messenger: DocMessenger, action: SessionStateAction, detail?: string): void { - if (detail) { - const progress = getFileSummaryPercentage(detail) - messenger.sendDocProgress( - this.tabID, - progress === 100 ? DocGenerationStep.GENERATING_ARTIFACTS : DocGenerationStep.SUMMARIZING_FILES, - progress, - action.mode - ) - } - } - - protected getScheme(): string { - return docScheme - } - - protected getTimeoutErrorCode(): string { - return 'DocGenerationTimeout' - } - - protected handleGenerationComplete( - messenger: DocMessenger, - newFileInfo: NewFileInfo[], - action: SessionStateAction - ): void { - messenger.sendDocProgress(this.tabID, DocGenerationStep.GENERATING_ARTIFACTS + 1, 100, action.mode) - } - - protected handleError(messenger: DocMessenger, codegenResult: any): Error { - // eslint-disable-next-line unicorn/no-null - messenger.sendUpdatePromptProgress(this.tabID, null) - - switch (true) { - case codegenResult.codeGenerationStatusDetail?.includes('README_TOO_LARGE'): { - return new ReadmeTooLargeError() - } - case codegenResult.codeGenerationStatusDetail?.includes('README_UPDATE_TOO_LARGE'): { - return new ReadmeUpdateTooLargeError(codegenResult.codeGenerationRemainingIterationCount || 0) - } - case codegenResult.codeGenerationStatusDetail?.includes('WORKSPACE_TOO_LARGE'): { - return new ContentLengthError() - } - case codegenResult.codeGenerationStatusDetail?.includes('WORKSPACE_EMPTY'): { - return new WorkspaceEmptyError() - } - case codegenResult.codeGenerationStatusDetail?.includes('PROMPT_UNRELATED'): { - return new PromptUnrelatedError(codegenResult.codeGenerationRemainingIterationCount || 0) - } - case codegenResult.codeGenerationStatusDetail?.includes('PROMPT_TOO_VAGUE'): { - return new PromptTooVagueError(codegenResult.codeGenerationRemainingIterationCount || 0) - } - case codegenResult.codeGenerationStatusDetail?.includes('PROMPT_REFUSAL'): { - return new PromptRefusalException(codegenResult.codeGenerationRemainingIterationCount || 0) - } - case codegenResult.codeGenerationStatusDetail?.includes('Guardrails'): { - return new ApiClientError( - i18n('AWS.amazonq.doc.error.docGen.default'), - 'GetTaskAssistCodeGeneration', - 'GuardrailsException', - 400 - ) - } - case codegenResult.codeGenerationStatusDetail?.includes('EmptyPatch'): { - if (codegenResult.codeGenerationStatusDetail?.includes('NO_CHANGE_REQUIRED')) { - return new NoChangeRequiredException() - } - - return new LlmError(i18n('AWS.amazonq.doc.error.docGen.default'), { - code: 'EmptyPatchException', - }) - } - case codegenResult.codeGenerationStatusDetail?.includes('Throttling'): { - return new ApiClientError( - i18n('AWS.amazonq.featureDev.error.throttling'), - 'GetTaskAssistCodeGeneration', - 'ThrottlingException', - 429 - ) - } - default: { - return new ApiServiceError( - i18n('AWS.amazonq.doc.error.docGen.default'), - 'GetTaskAssistCodeGeneration', - 'UnknownException', - 500 - ) - } - } - } - - protected async startCodeGeneration(action: SessionStateAction, codeGenerationId: string): Promise { - if (!action.tokenSource?.token.isCancellationRequested) { - action.messenger.sendDocProgress(this.tabID, DocGenerationStep.SUMMARIZING_FILES, 0, action.mode as Mode) - } - - await this.config.proxyClient.startCodeGeneration( - this.config.conversationId, - this.config.uploadId, - action.msg, - Intent.DOC, - codeGenerationId, - undefined, - action.folderPath ? { documentation: { type: 'README', scope: action.folderPath } } : undefined - ) - } - - protected override createNextState(config: SessionStateConfig, params: CreateNextStateParams): SessionState { - return super.createNextState(config, params, DocPrepareCodeGenState) - } -} - -export class DocPrepareCodeGenState extends BasePrepareCodeGenState { - protected preUpload(action: SessionStateAction): void { - // Do nothing - } - - protected postUpload(action: SessionStateAction): void { - // Do nothing - } - - protected override createNextState(config: SessionStateConfig): SessionState { - return super.createNextState(config, DocCodeGenState) - } - - protected override async prepareProjectZip( - workspaceRoots: string[], - workspaceFolders: CurrentWsFolders, - span: Span, - options: PrepareRepoDataOptions - ) { - return await prepareRepoData(workspaceRoots, workspaceFolders, span, { - ...options, - isIncludeInfraDiagram: true, - }) - } -} diff --git a/packages/core/src/amazonqDoc/storages/chatSession.ts b/packages/core/src/amazonqDoc/storages/chatSession.ts deleted file mode 100644 index 34fb9f5404e..00000000000 --- a/packages/core/src/amazonqDoc/storages/chatSession.ts +++ /dev/null @@ -1,23 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { BaseChatSessionStorage } from '../../amazonq/commons/baseChatStorage' -import { createSessionConfig } from '../../amazonq/commons/session/sessionConfigFactory' -import { docScheme } from '../constants' -import { DocMessenger } from '../messenger' -import { Session } from '../session/session' - -export class DocChatSessionStorage extends BaseChatSessionStorage { - constructor(protected readonly messenger: DocMessenger) { - super() - } - - override async createSession(tabID: string): Promise { - const sessionConfig = await createSessionConfig(docScheme) - const session = new Session(sessionConfig, this.messenger, tabID) - this.sessions.set(tabID, session) - return session - } -} diff --git a/packages/core/src/amazonqDoc/types.ts b/packages/core/src/amazonqDoc/types.ts deleted file mode 100644 index 005353d0e23..00000000000 --- a/packages/core/src/amazonqDoc/types.ts +++ /dev/null @@ -1,83 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { ChatItemButton, MynahIcons, ProgressField } from '@aws/mynah-ui' -import { - LLMResponseType, - SessionStorage, - SessionInfo, - DeletedFileInfo, - NewFileInfo, - NewFileZipContents, - SessionStateConfig, - SessionStatePhase, - DevPhase, - Interaction, - CurrentWsFolders, - CodeGenerationStatus, - SessionState as FeatureDevSessionState, - SessionStateAction as FeatureDevSessionStateAction, - SessionStateInteraction as FeatureDevSessionStateInteraction, -} from '../amazonq/commons/types' - -import { Mode } from './constants' -import { DocMessenger } from './messenger' - -export const cancelDocGenButton: ChatItemButton = { - id: 'cancel-doc-generation', - text: 'Cancel', - icon: 'cancel' as MynahIcons, -} - -export const inProgress = (progress: number, text: string): ProgressField => { - return { - status: 'default', - text, - value: progress === 100 ? -1 : progress, - actions: [cancelDocGenButton], - } -} - -export interface SessionStateInteraction extends FeatureDevSessionStateInteraction { - nextState: SessionState | Omit | undefined - interaction: Interaction -} - -export interface SessionState extends FeatureDevSessionState { - interact(action: SessionStateAction): Promise -} - -export interface SessionStateAction extends FeatureDevSessionStateAction { - messenger: DocMessenger - mode: Mode - folderPath?: string -} - -export enum MetricDataOperationName { - StartDocGeneration = 'StartDocGeneration', - EndDocGeneration = 'EndDocGeneration', -} - -export enum MetricDataResult { - Success = 'Success', - Fault = 'Fault', - Error = 'Error', - LlmFailure = 'LLMFailure', -} - -export { - LLMResponseType, - SessionStorage, - SessionInfo, - DeletedFileInfo, - NewFileInfo, - NewFileZipContents, - SessionStateConfig, - SessionStatePhase, - DevPhase, - Interaction, - CodeGenerationStatus, - CurrentWsFolders, -} diff --git a/packages/core/src/amazonqDoc/views/actions/uiMessageListener.ts b/packages/core/src/amazonqDoc/views/actions/uiMessageListener.ts deleted file mode 100644 index c6960b15fcc..00000000000 --- a/packages/core/src/amazonqDoc/views/actions/uiMessageListener.ts +++ /dev/null @@ -1,168 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { ChatControllerEventEmitters } from '../../controllers/chat/controller' -import { MessageListener } from '../../../amazonq/messages/messageListener' -import { ExtensionMessage } from '../../../amazonq/webview/ui/commands' - -export interface UIMessageListenerProps { - readonly chatControllerEventEmitters: ChatControllerEventEmitters - readonly webViewMessageListener: MessageListener -} - -export class UIMessageListener { - private docGenerationControllerEventsEmitters: ChatControllerEventEmitters | undefined - private webViewMessageListener: MessageListener - - constructor(props: UIMessageListenerProps) { - this.docGenerationControllerEventsEmitters = props.chatControllerEventEmitters - this.webViewMessageListener = props.webViewMessageListener - - // Now we are listening to events that get sent from amazonq/webview/actions/actionListener (e.g. the tab) - this.webViewMessageListener.onMessage((msg) => { - this.handleMessage(msg) - }) - } - - private handleMessage(msg: ExtensionMessage) { - switch (msg.command) { - case 'chat-prompt': - this.processChatMessage(msg) - break - case 'follow-up-was-clicked': - this.followUpClicked(msg) - break - case 'open-diff': - this.openDiff(msg) - break - case 'chat-item-voted': - this.chatItemVoted(msg) - break - case 'chat-item-feedback': - this.chatItemFeedback(msg) - break - case 'stop-response': - this.stopResponse(msg) - break - case 'new-tab-was-created': - this.tabOpened(msg) - break - case 'tab-was-removed': - this.tabClosed(msg) - break - case 'auth-follow-up-was-clicked': - this.authClicked(msg) - break - case 'response-body-link-click': - this.processResponseBodyLinkClick(msg) - break - case 'insert_code_at_cursor_position': - this.insertCodeAtPosition(msg) - break - case 'file-click': - this.fileClicked(msg) - break - case 'form-action-click': - this.formActionClicked(msg) - break - } - } - - private chatItemVoted(msg: any) { - this.docGenerationControllerEventsEmitters?.processChatItemVotedMessage.fire({ - tabID: msg.tabID, - command: msg.command, - vote: msg.vote, - messageId: msg.messageId, - }) - } - - private chatItemFeedback(msg: any) { - this.docGenerationControllerEventsEmitters?.processChatItemFeedbackMessage.fire(msg) - } - - private processChatMessage(msg: any) { - this.docGenerationControllerEventsEmitters?.processHumanChatMessage.fire({ - message: msg.chatMessage, - tabID: msg.tabID, - }) - } - - private followUpClicked(msg: any) { - this.docGenerationControllerEventsEmitters?.followUpClicked.fire({ - followUp: msg.followUp, - tabID: msg.tabID, - }) - } - - private formActionClicked(msg: any) { - this.docGenerationControllerEventsEmitters?.formActionClicked.fire({ - ...msg, - }) - } - - private fileClicked(msg: any) { - this.docGenerationControllerEventsEmitters?.fileClicked.fire({ - tabID: msg.tabID, - filePath: msg.filePath, - actionName: msg.actionName, - messageId: msg.messageId, - }) - } - - private openDiff(msg: any) { - this.docGenerationControllerEventsEmitters?.openDiff.fire({ - tabID: msg.tabID, - filePath: msg.filePath, - deleted: msg.deleted, - messageId: msg.messageId, - }) - } - - private stopResponse(msg: any) { - this.docGenerationControllerEventsEmitters?.stopResponse.fire({ - tabID: msg.tabID, - }) - } - - private tabOpened(msg: any) { - this.docGenerationControllerEventsEmitters?.tabOpened.fire({ - tabID: msg.tabID, - }) - } - - private tabClosed(msg: any) { - this.docGenerationControllerEventsEmitters?.tabClosed.fire({ - tabID: msg.tabID, - }) - } - - private authClicked(msg: any) { - this.docGenerationControllerEventsEmitters?.authClicked.fire({ - tabID: msg.tabID, - authType: msg.authType, - }) - } - - private processResponseBodyLinkClick(msg: any) { - this.docGenerationControllerEventsEmitters?.processResponseBodyLinkClick.fire({ - command: msg.command, - messageId: msg.messageId, - tabID: msg.tabID, - link: msg.link, - }) - } - - private insertCodeAtPosition(msg: any) { - this.docGenerationControllerEventsEmitters?.insertCodeAtPositionClicked.fire({ - command: msg.command, - messageId: msg.messageId, - tabID: msg.tabID, - code: msg.code, - insertionTargetType: msg.insertionTargetType, - codeReference: msg.codeReference, - }) - } -} diff --git a/packages/core/src/amazonqFeatureDev/app.ts b/packages/core/src/amazonqFeatureDev/app.ts deleted file mode 100644 index fd0652fd0e4..00000000000 --- a/packages/core/src/amazonqFeatureDev/app.ts +++ /dev/null @@ -1,106 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import { UIMessageListener } from './views/actions/uiMessageListener' -import { ChatControllerEventEmitters, FeatureDevController } from './controllers/chat/controller' -import { AmazonQAppInitContext } from '../amazonq/apps/initContext' -import { MessageListener } from '../amazonq/messages/messageListener' -import { fromQueryToParameters } from '../shared/utilities/uriUtils' -import { getLogger } from '../shared/logger/logger' -import { TabIdNotFoundError } from './errors' -import { featureDevChat, featureDevScheme } from './constants' -import globals from '../shared/extensionGlobals' -import { FeatureDevChatSessionStorage } from './storages/chatSession' -import { AuthUtil } from '../codewhisperer/util/authUtil' -import { debounce } from 'lodash' -import { Messenger } from '../amazonq/commons/connector/baseMessenger' -import { AppToWebViewMessageDispatcher } from '../amazonq/commons/connector/connectorMessages' - -export function init(appContext: AmazonQAppInitContext) { - const featureDevChatControllerEventEmitters: ChatControllerEventEmitters = { - processHumanChatMessage: new vscode.EventEmitter(), - followUpClicked: new vscode.EventEmitter(), - openDiff: new vscode.EventEmitter(), - processChatItemVotedMessage: new vscode.EventEmitter(), - processChatItemFeedbackMessage: new vscode.EventEmitter(), - stopResponse: new vscode.EventEmitter(), - tabOpened: new vscode.EventEmitter(), - tabClosed: new vscode.EventEmitter(), - authClicked: new vscode.EventEmitter(), - processResponseBodyLinkClick: new vscode.EventEmitter(), - insertCodeAtPositionClicked: new vscode.EventEmitter(), - fileClicked: new vscode.EventEmitter(), - storeCodeResultMessageId: new vscode.EventEmitter(), - } - - const messenger = new Messenger( - new AppToWebViewMessageDispatcher(appContext.getAppsToWebViewMessagePublisher()), - featureDevChat - ) - const sessionStorage = new FeatureDevChatSessionStorage(messenger) - - new FeatureDevController( - featureDevChatControllerEventEmitters, - messenger, - sessionStorage, - appContext.onDidChangeAmazonQVisibility.event - ) - - const featureDevProvider = new (class implements vscode.TextDocumentContentProvider { - async provideTextDocumentContent(uri: vscode.Uri): Promise { - const params = fromQueryToParameters(uri.query) - - const tabID = params.get('tabID') - if (!tabID) { - getLogger().error(`Unable to find tabID from ${uri.toString()}`) - throw new TabIdNotFoundError() - } - - const session = await sessionStorage.getSession(tabID) - const content = await session.config.fs.readFile(uri) - const decodedContent = new TextDecoder().decode(content) - return decodedContent - } - })() - - const textDocumentProvider = vscode.workspace.registerTextDocumentContentProvider( - featureDevScheme, - featureDevProvider - ) - - globals.context.subscriptions.push(textDocumentProvider) - - const featureDevChatUIInputEventEmitter = new vscode.EventEmitter() - - new UIMessageListener({ - chatControllerEventEmitters: featureDevChatControllerEventEmitters, - webViewMessageListener: new MessageListener(featureDevChatUIInputEventEmitter), - }) - - const debouncedEvent = debounce(async () => { - const authenticated = (await AuthUtil.instance.getChatAuthState()).amazonQ === 'connected' - let authenticatingSessionIDs: string[] = [] - if (authenticated) { - const authenticatingSessions = sessionStorage.getAuthenticatingSessions() - - authenticatingSessionIDs = authenticatingSessions.map((session) => session.tabID) - - // We've already authenticated these sessions - for (const session of authenticatingSessions) { - session.isAuthenticating = false - } - } - - messenger.sendAuthenticationUpdate(authenticated, authenticatingSessionIDs) - }, 500) - - AuthUtil.instance.secondaryAuth.onDidChangeActiveConnection(() => { - return debouncedEvent() - }) - AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(() => { - return debouncedEvent() - }) -} diff --git a/packages/core/src/amazonqFeatureDev/client/codewhispererruntime-2022-11-11.json b/packages/core/src/amazonqFeatureDev/client/codewhispererruntime-2022-11-11.json deleted file mode 100644 index 812bbd4fd69..00000000000 --- a/packages/core/src/amazonqFeatureDev/client/codewhispererruntime-2022-11-11.json +++ /dev/null @@ -1,5640 +0,0 @@ -{ - "version": "2.0", - "metadata": { - "apiVersion": "2022-11-11", - "auth": ["smithy.api#httpBearerAuth"], - "endpointPrefix": "amazoncodewhispererservice", - "jsonVersion": "1.0", - "protocol": "json", - "protocols": ["json"], - "serviceFullName": "Amazon CodeWhisperer", - "serviceId": "CodeWhispererRuntime", - "signatureVersion": "bearer", - "signingName": "amazoncodewhispererservice", - "targetPrefix": "AmazonCodeWhispererService", - "uid": "codewhispererruntime-2022-11-11" - }, - "operations": { - "CreateArtifactUploadUrl": { - "name": "CreateArtifactUploadUrl", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "CreateUploadUrlRequest" - }, - "output": { - "shape": "CreateUploadUrlResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ], - "idempotent": true - }, - "CreateTaskAssistConversation": { - "name": "CreateTaskAssistConversation", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "CreateTaskAssistConversationRequest" - }, - "output": { - "shape": "CreateTaskAssistConversationResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ServiceQuotaExceededException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "CreateUploadUrl": { - "name": "CreateUploadUrl", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "CreateUploadUrlRequest" - }, - "output": { - "shape": "CreateUploadUrlResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ConflictException" - }, - { - "shape": "ServiceQuotaExceededException" - }, - { - "shape": "ResourceNotFoundException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ], - "idempotent": true - }, - "CreateUserMemoryEntry": { - "name": "CreateUserMemoryEntry", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "CreateUserMemoryEntryInput" - }, - "output": { - "shape": "CreateUserMemoryEntryOutput" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ], - "idempotent": true - }, - "CreateWorkspace": { - "name": "CreateWorkspace", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "CreateWorkspaceRequest" - }, - "output": { - "shape": "CreateWorkspaceResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ConflictException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "DeleteTaskAssistConversation": { - "name": "DeleteTaskAssistConversation", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "DeleteTaskAssistConversationRequest" - }, - "output": { - "shape": "DeleteTaskAssistConversationResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ResourceNotFoundException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "DeleteUserMemoryEntry": { - "name": "DeleteUserMemoryEntry", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "DeleteUserMemoryEntryInput" - }, - "output": { - "shape": "DeleteUserMemoryEntryOutput" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ResourceNotFoundException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ], - "idempotent": true - }, - "DeleteWorkspace": { - "name": "DeleteWorkspace", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "DeleteWorkspaceRequest" - }, - "output": { - "shape": "DeleteWorkspaceResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "GenerateCompletions": { - "name": "GenerateCompletions", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "GenerateCompletionsRequest" - }, - "output": { - "shape": "GenerateCompletionsResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "GetCodeAnalysis": { - "name": "GetCodeAnalysis", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "GetCodeAnalysisRequest" - }, - "output": { - "shape": "GetCodeAnalysisResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ResourceNotFoundException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "GetCodeFixJob": { - "name": "GetCodeFixJob", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "GetCodeFixJobRequest" - }, - "output": { - "shape": "GetCodeFixJobResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ResourceNotFoundException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "GetTaskAssistCodeGeneration": { - "name": "GetTaskAssistCodeGeneration", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "GetTaskAssistCodeGenerationRequest" - }, - "output": { - "shape": "GetTaskAssistCodeGenerationResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ConflictException" - }, - { - "shape": "ResourceNotFoundException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "GetTestGeneration": { - "name": "GetTestGeneration", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "GetTestGenerationRequest" - }, - "output": { - "shape": "GetTestGenerationResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "GetTransformation": { - "name": "GetTransformation", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "GetTransformationRequest" - }, - "output": { - "shape": "GetTransformationResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ResourceNotFoundException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "GetTransformationPlan": { - "name": "GetTransformationPlan", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "GetTransformationPlanRequest" - }, - "output": { - "shape": "GetTransformationPlanResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ResourceNotFoundException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "ListAvailableCustomizations": { - "name": "ListAvailableCustomizations", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "ListAvailableCustomizationsRequest" - }, - "output": { - "shape": "ListAvailableCustomizationsResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "ListAvailableProfiles": { - "name": "ListAvailableProfiles", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "ListAvailableProfilesRequest" - }, - "output": { - "shape": "ListAvailableProfilesResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "ListCodeAnalysisFindings": { - "name": "ListCodeAnalysisFindings", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "ListCodeAnalysisFindingsRequest" - }, - "output": { - "shape": "ListCodeAnalysisFindingsResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ResourceNotFoundException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "ListEvents": { - "name": "ListEvents", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "ListEventsRequest" - }, - "output": { - "shape": "ListEventsResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "ListFeatureEvaluations": { - "name": "ListFeatureEvaluations", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "ListFeatureEvaluationsRequest" - }, - "output": { - "shape": "ListFeatureEvaluationsResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "ListUserMemoryEntries": { - "name": "ListUserMemoryEntries", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "ListUserMemoryEntriesInput" - }, - "output": { - "shape": "ListUserMemoryEntriesOutput" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "ListWorkspaceMetadata": { - "name": "ListWorkspaceMetadata", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "ListWorkspaceMetadataRequest" - }, - "output": { - "shape": "ListWorkspaceMetadataResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "ResumeTransformation": { - "name": "ResumeTransformation", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "ResumeTransformationRequest" - }, - "output": { - "shape": "ResumeTransformationResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ResourceNotFoundException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "SendTelemetryEvent": { - "name": "SendTelemetryEvent", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "SendTelemetryEventRequest" - }, - "output": { - "shape": "SendTelemetryEventResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ], - "idempotent": true - }, - "StartCodeAnalysis": { - "name": "StartCodeAnalysis", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "StartCodeAnalysisRequest" - }, - "output": { - "shape": "StartCodeAnalysisResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ConflictException" - }, - { - "shape": "ResourceNotFoundException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ], - "idempotent": true - }, - "StartCodeFixJob": { - "name": "StartCodeFixJob", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "StartCodeFixJobRequest" - }, - "output": { - "shape": "StartCodeFixJobResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "StartTaskAssistCodeGeneration": { - "name": "StartTaskAssistCodeGeneration", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "StartTaskAssistCodeGenerationRequest" - }, - "output": { - "shape": "StartTaskAssistCodeGenerationResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ConflictException" - }, - { - "shape": "ServiceQuotaExceededException" - }, - { - "shape": "ResourceNotFoundException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "StartTestGeneration": { - "name": "StartTestGeneration", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "StartTestGenerationRequest" - }, - "output": { - "shape": "StartTestGenerationResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ConflictException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ], - "idempotent": true - }, - "StartTransformation": { - "name": "StartTransformation", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "StartTransformationRequest" - }, - "output": { - "shape": "StartTransformationResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ConflictException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - }, - "StopTransformation": { - "name": "StopTransformation", - "http": { - "method": "POST", - "requestUri": "/" - }, - "input": { - "shape": "StopTransformationRequest" - }, - "output": { - "shape": "StopTransformationResponse" - }, - "errors": [ - { - "shape": "ThrottlingException" - }, - { - "shape": "ResourceNotFoundException" - }, - { - "shape": "InternalServerException" - }, - { - "shape": "ValidationException" - }, - { - "shape": "AccessDeniedException" - } - ] - } - }, - "shapes": { - "AccessDeniedException": { - "type": "structure", - "required": ["message"], - "members": { - "message": { - "shape": "String" - }, - "reason": { - "shape": "AccessDeniedExceptionReason" - } - }, - "exception": true - }, - "AccessDeniedExceptionReason": { - "type": "string", - "enum": ["UNAUTHORIZED_CUSTOMIZATION_RESOURCE_ACCESS"] - }, - "ActiveFunctionalityList": { - "type": "list", - "member": { - "shape": "FunctionalityName" - }, - "max": 10, - "min": 0 - }, - "AdditionalContentEntry": { - "type": "structure", - "required": ["name", "description"], - "members": { - "name": { - "shape": "AdditionalContentEntryNameString" - }, - "description": { - "shape": "AdditionalContentEntryDescriptionString" - }, - "innerContext": { - "shape": "AdditionalContentEntryInnerContextString" - } - } - }, - "AdditionalContentEntryDescriptionString": { - "type": "string", - "max": 1024, - "min": 1, - "sensitive": true - }, - "AdditionalContentEntryInnerContextString": { - "type": "string", - "max": 8192, - "min": 1, - "sensitive": true - }, - "AdditionalContentEntryNameString": { - "type": "string", - "max": 1024, - "min": 1, - "pattern": "[a-z]+(?:-[a-z0-9]+)*", - "sensitive": true - }, - "AdditionalContentList": { - "type": "list", - "member": { - "shape": "AdditionalContentEntry" - }, - "max": 20, - "min": 0 - }, - "AppStudioState": { - "type": "structure", - "required": ["namespace", "propertyName", "propertyContext"], - "members": { - "namespace": { - "shape": "AppStudioStateNamespaceString" - }, - "propertyName": { - "shape": "AppStudioStatePropertyNameString" - }, - "propertyValue": { - "shape": "AppStudioStatePropertyValueString" - }, - "propertyContext": { - "shape": "AppStudioStatePropertyContextString" - } - } - }, - "AppStudioStateNamespaceString": { - "type": "string", - "max": 1024, - "min": 1, - "sensitive": true - }, - "AppStudioStatePropertyContextString": { - "type": "string", - "max": 1024, - "min": 1, - "sensitive": true - }, - "AppStudioStatePropertyNameString": { - "type": "string", - "max": 1024, - "min": 1, - "sensitive": true - }, - "AppStudioStatePropertyValueString": { - "type": "string", - "max": 10240, - "min": 0, - "sensitive": true - }, - "ApplicationProperties": { - "type": "structure", - "required": ["tenantId", "applicationArn", "tenantUrl", "applicationType"], - "members": { - "tenantId": { - "shape": "TenantId" - }, - "applicationArn": { - "shape": "ResourceArn" - }, - "tenantUrl": { - "shape": "Url" - }, - "applicationType": { - "shape": "FunctionalityName" - } - } - }, - "ApplicationPropertiesList": { - "type": "list", - "member": { - "shape": "ApplicationProperties" - } - }, - "ArtifactId": { - "type": "string", - "max": 126, - "min": 1, - "pattern": "[a-zA-Z0-9-_]+" - }, - "ArtifactMap": { - "type": "map", - "key": { - "shape": "ArtifactType" - }, - "value": { - "shape": "UploadId" - }, - "max": 64, - "min": 1 - }, - "ArtifactType": { - "type": "string", - "enum": ["SourceCode", "BuiltJars"] - }, - "AssistantResponseMessage": { - "type": "structure", - "required": ["content"], - "members": { - "messageId": { - "shape": "MessageId" - }, - "content": { - "shape": "AssistantResponseMessageContentString" - }, - "supplementaryWebLinks": { - "shape": "SupplementaryWebLinks" - }, - "references": { - "shape": "References" - }, - "followupPrompt": { - "shape": "FollowupPrompt" - }, - "toolUses": { - "shape": "ToolUses" - } - } - }, - "AssistantResponseMessageContentString": { - "type": "string", - "max": 100000, - "min": 0, - "sensitive": true - }, - "AttributesMap": { - "type": "map", - "key": { - "shape": "AttributesMapKeyString" - }, - "value": { - "shape": "StringList" - } - }, - "AttributesMapKeyString": { - "type": "string", - "max": 128, - "min": 1 - }, - "Base64EncodedPaginationToken": { - "type": "string", - "max": 2048, - "min": 1, - "pattern": "(?:[A-Za-z0-9\\+/]{4})*(?:[A-Za-z0-9\\+/]{2}\\=\\=|[A-Za-z0-9\\+/]{3}\\=)?" - }, - "Boolean": { - "type": "boolean", - "box": true - }, - "ByUserAnalytics": { - "type": "structure", - "required": ["toggle"], - "members": { - "s3Uri": { - "shape": "S3Uri" - }, - "toggle": { - "shape": "OptInFeatureToggle" - } - } - }, - "ChatAddMessageEvent": { - "type": "structure", - "required": ["conversationId", "messageId"], - "members": { - "conversationId": { - "shape": "ConversationId" - }, - "messageId": { - "shape": "MessageId" - }, - "customizationArn": { - "shape": "CustomizationArn" - }, - "userIntent": { - "shape": "UserIntent" - }, - "hasCodeSnippet": { - "shape": "Boolean" - }, - "programmingLanguage": { - "shape": "ProgrammingLanguage" - }, - "activeEditorTotalCharacters": { - "shape": "Integer" - }, - "timeToFirstChunkMilliseconds": { - "shape": "Double" - }, - "timeBetweenChunks": { - "shape": "timeBetweenChunks" - }, - "fullResponselatency": { - "shape": "Double" - }, - "requestLength": { - "shape": "Integer" - }, - "responseLength": { - "shape": "Integer" - }, - "numberOfCodeBlocks": { - "shape": "Integer" - }, - "hasProjectLevelContext": { - "shape": "Boolean" - } - } - }, - "ChatHistory": { - "type": "list", - "member": { - "shape": "ChatMessage" - }, - "max": 250, - "min": 0 - }, - "ChatInteractWithMessageEvent": { - "type": "structure", - "required": ["conversationId", "messageId"], - "members": { - "conversationId": { - "shape": "ConversationId" - }, - "messageId": { - "shape": "MessageId" - }, - "customizationArn": { - "shape": "CustomizationArn" - }, - "interactionType": { - "shape": "ChatMessageInteractionType" - }, - "interactionTarget": { - "shape": "ChatInteractWithMessageEventInteractionTargetString" - }, - "acceptedCharacterCount": { - "shape": "Integer" - }, - "acceptedLineCount": { - "shape": "Integer" - }, - "acceptedSnippetHasReference": { - "shape": "Boolean" - }, - "hasProjectLevelContext": { - "shape": "Boolean" - }, - "userIntent": { - "shape": "UserIntent" - }, - "addedIdeDiagnostics": { - "shape": "IdeDiagnosticList" - }, - "removedIdeDiagnostics": { - "shape": "IdeDiagnosticList" - } - } - }, - "ChatInteractWithMessageEventInteractionTargetString": { - "type": "string", - "max": 1024, - "min": 1 - }, - "ChatMessage": { - "type": "structure", - "members": { - "userInputMessage": { - "shape": "UserInputMessage" - }, - "assistantResponseMessage": { - "shape": "AssistantResponseMessage" - } - }, - "union": true - }, - "ChatMessageInteractionType": { - "type": "string", - "enum": [ - "INSERT_AT_CURSOR", - "COPY_SNIPPET", - "COPY", - "CLICK_LINK", - "CLICK_BODY_LINK", - "CLICK_FOLLOW_UP", - "HOVER_REFERENCE", - "UPVOTE", - "DOWNVOTE" - ] - }, - "ChatTriggerType": { - "type": "string", - "enum": ["MANUAL", "DIAGNOSTIC", "INLINE_CHAT"] - }, - "ChatUserModificationEvent": { - "type": "structure", - "required": ["conversationId", "messageId", "modificationPercentage"], - "members": { - "conversationId": { - "shape": "ConversationId" - }, - "customizationArn": { - "shape": "CustomizationArn" - }, - "messageId": { - "shape": "MessageId" - }, - "programmingLanguage": { - "shape": "ProgrammingLanguage" - }, - "modificationPercentage": { - "shape": "Double" - }, - "hasProjectLevelContext": { - "shape": "Boolean" - } - } - }, - "ClientId": { - "type": "string", - "max": 255, - "min": 1 - }, - "CodeAnalysisFindingsSchema": { - "type": "string", - "enum": ["codeanalysis/findings/1.0"] - }, - "CodeAnalysisScope": { - "type": "string", - "enum": ["FILE", "PROJECT"] - }, - "CodeAnalysisStatus": { - "type": "string", - "enum": ["Completed", "Pending", "Failed"] - }, - "CodeAnalysisUploadContext": { - "type": "structure", - "required": ["codeScanName"], - "members": { - "codeScanName": { - "shape": "CodeScanName" - } - } - }, - "CodeCoverageEvent": { - "type": "structure", - "required": ["programmingLanguage", "acceptedCharacterCount", "totalCharacterCount", "timestamp"], - "members": { - "customizationArn": { - "shape": "CustomizationArn" - }, - "programmingLanguage": { - "shape": "ProgrammingLanguage" - }, - "acceptedCharacterCount": { - "shape": "PrimitiveInteger" - }, - "totalCharacterCount": { - "shape": "PrimitiveInteger" - }, - "timestamp": { - "shape": "Timestamp" - }, - "unmodifiedAcceptedCharacterCount": { - "shape": "PrimitiveInteger" - }, - "totalNewCodeCharacterCount": { - "shape": "PrimitiveInteger" - }, - "totalNewCodeLineCount": { - "shape": "PrimitiveInteger" - }, - "userWrittenCodeCharacterCount": { - "shape": "CodeCoverageEventUserWrittenCodeCharacterCountInteger" - }, - "userWrittenCodeLineCount": { - "shape": "CodeCoverageEventUserWrittenCodeLineCountInteger" - } - } - }, - "CodeCoverageEventUserWrittenCodeCharacterCountInteger": { - "type": "integer", - "min": 0 - }, - "CodeCoverageEventUserWrittenCodeLineCountInteger": { - "type": "integer", - "min": 0 - }, - "CodeDescription": { - "type": "structure", - "required": ["href"], - "members": { - "href": { - "shape": "CodeDescriptionHrefString" - } - } - }, - "CodeDescriptionHrefString": { - "type": "string", - "max": 1024, - "min": 1, - "sensitive": true - }, - "CodeFixAcceptanceEvent": { - "type": "structure", - "required": ["jobId"], - "members": { - "jobId": { - "shape": "String" - }, - "ruleId": { - "shape": "String" - }, - "detectorId": { - "shape": "String" - }, - "findingId": { - "shape": "String" - }, - "programmingLanguage": { - "shape": "ProgrammingLanguage" - }, - "linesOfCodeAccepted": { - "shape": "Integer" - }, - "charsOfCodeAccepted": { - "shape": "Integer" - } - } - }, - "CodeFixGenerationEvent": { - "type": "structure", - "required": ["jobId"], - "members": { - "jobId": { - "shape": "String" - }, - "ruleId": { - "shape": "String" - }, - "detectorId": { - "shape": "String" - }, - "findingId": { - "shape": "String" - }, - "programmingLanguage": { - "shape": "ProgrammingLanguage" - }, - "linesOfCodeGenerated": { - "shape": "Integer" - }, - "charsOfCodeGenerated": { - "shape": "Integer" - } - } - }, - "CodeFixJobStatus": { - "type": "string", - "enum": ["Succeeded", "InProgress", "Failed"] - }, - "CodeFixName": { - "type": "string", - "max": 128, - "min": 1, - "pattern": "[a-zA-Z0-9-_$:.]*" - }, - "CodeFixUploadContext": { - "type": "structure", - "required": ["codeFixName"], - "members": { - "codeFixName": { - "shape": "CodeFixName" - } - } - }, - "CodeGenerationId": { - "type": "string", - "max": 128, - "min": 1 - }, - "CodeGenerationStatus": { - "type": "structure", - "required": ["status", "currentStage"], - "members": { - "status": { - "shape": "CodeGenerationWorkflowStatus" - }, - "currentStage": { - "shape": "CodeGenerationWorkflowStage" - } - } - }, - "CodeGenerationStatusDetail": { - "type": "string", - "sensitive": true - }, - "CodeGenerationWorkflowStage": { - "type": "string", - "enum": ["InitialCodeGeneration", "CodeRefinement"] - }, - "CodeGenerationWorkflowStatus": { - "type": "string", - "enum": ["InProgress", "Complete", "Failed"] - }, - "CodeScanEvent": { - "type": "structure", - "required": ["programmingLanguage", "codeScanJobId", "timestamp"], - "members": { - "programmingLanguage": { - "shape": "ProgrammingLanguage" - }, - "codeScanJobId": { - "shape": "CodeScanJobId" - }, - "timestamp": { - "shape": "Timestamp" - }, - "codeAnalysisScope": { - "shape": "CodeAnalysisScope" - } - } - }, - "CodeScanFailedEvent": { - "type": "structure", - "required": ["programmingLanguage", "codeScanJobId", "timestamp"], - "members": { - "programmingLanguage": { - "shape": "ProgrammingLanguage" - }, - "codeScanJobId": { - "shape": "CodeScanJobId" - }, - "timestamp": { - "shape": "Timestamp" - }, - "codeAnalysisScope": { - "shape": "CodeAnalysisScope" - } - } - }, - "CodeScanJobId": { - "type": "string", - "max": 128, - "min": 1 - }, - "CodeScanName": { - "type": "string", - "max": 128, - "min": 1 - }, - "CodeScanRemediationsEvent": { - "type": "structure", - "members": { - "programmingLanguage": { - "shape": "ProgrammingLanguage" - }, - "CodeScanRemediationsEventType": { - "shape": "CodeScanRemediationsEventType" - }, - "timestamp": { - "shape": "Timestamp" - }, - "detectorId": { - "shape": "String" - }, - "findingId": { - "shape": "String" - }, - "ruleId": { - "shape": "String" - }, - "component": { - "shape": "String" - }, - "reason": { - "shape": "String" - }, - "result": { - "shape": "String" - }, - "includesFix": { - "shape": "Boolean" - } - } - }, - "CodeScanRemediationsEventType": { - "type": "string", - "enum": ["CODESCAN_ISSUE_HOVER", "CODESCAN_ISSUE_APPLY_FIX", "CODESCAN_ISSUE_VIEW_DETAILS"] - }, - "CodeScanSucceededEvent": { - "type": "structure", - "required": ["programmingLanguage", "codeScanJobId", "timestamp", "numberOfFindings"], - "members": { - "programmingLanguage": { - "shape": "ProgrammingLanguage" - }, - "codeScanJobId": { - "shape": "CodeScanJobId" - }, - "timestamp": { - "shape": "Timestamp" - }, - "numberOfFindings": { - "shape": "PrimitiveInteger" - }, - "codeAnalysisScope": { - "shape": "CodeAnalysisScope" - } - } - }, - "Completion": { - "type": "structure", - "required": ["content"], - "members": { - "content": { - "shape": "CompletionContentString" - }, - "references": { - "shape": "References" - }, - "mostRelevantMissingImports": { - "shape": "Imports" - } - } - }, - "CompletionContentString": { - "type": "string", - "max": 5120, - "min": 1, - "sensitive": true - }, - "CompletionType": { - "type": "string", - "enum": ["BLOCK", "LINE"] - }, - "Completions": { - "type": "list", - "member": { - "shape": "Completion" - }, - "max": 10, - "min": 0 - }, - "ConflictException": { - "type": "structure", - "required": ["message"], - "members": { - "message": { - "shape": "String" - }, - "reason": { - "shape": "ConflictExceptionReason" - } - }, - "exception": true - }, - "ConflictExceptionReason": { - "type": "string", - "enum": ["CUSTOMER_KMS_KEY_INVALID_KEY_POLICY", "CUSTOMER_KMS_KEY_DISABLED", "MISMATCHED_KMS_KEY"] - }, - "ConsoleState": { - "type": "structure", - "members": { - "region": { - "shape": "String" - }, - "consoleUrl": { - "shape": "SensitiveString" - }, - "serviceId": { - "shape": "String" - }, - "serviceConsolePage": { - "shape": "String" - }, - "serviceSubconsolePage": { - "shape": "String" - }, - "taskName": { - "shape": "SensitiveString" - } - } - }, - "ContentChecksumType": { - "type": "string", - "enum": ["SHA_256"] - }, - "ContextTruncationScheme": { - "type": "string", - "enum": ["ANALYSIS", "GUMBY"] - }, - "ConversationId": { - "type": "string", - "max": 128, - "min": 1 - }, - "ConversationState": { - "type": "structure", - "required": ["currentMessage", "chatTriggerType"], - "members": { - "conversationId": { - "shape": "ConversationId" - }, - "history": { - "shape": "ChatHistory" - }, - "currentMessage": { - "shape": "ChatMessage" - }, - "chatTriggerType": { - "shape": "ChatTriggerType" - }, - "customizationArn": { - "shape": "ResourceArn" - } - } - }, - "CreateTaskAssistConversationRequest": { - "type": "structure", - "members": { - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "CreateTaskAssistConversationResponse": { - "type": "structure", - "required": ["conversationId"], - "members": { - "conversationId": { - "shape": "ConversationId" - } - } - }, - "CreateUploadUrlRequest": { - "type": "structure", - "members": { - "contentMd5": { - "shape": "CreateUploadUrlRequestContentMd5String" - }, - "contentChecksum": { - "shape": "CreateUploadUrlRequestContentChecksumString" - }, - "contentChecksumType": { - "shape": "ContentChecksumType" - }, - "contentLength": { - "shape": "CreateUploadUrlRequestContentLengthLong" - }, - "artifactType": { - "shape": "ArtifactType" - }, - "uploadIntent": { - "shape": "UploadIntent" - }, - "uploadContext": { - "shape": "UploadContext" - }, - "uploadId": { - "shape": "UploadId" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "CreateUploadUrlRequestContentChecksumString": { - "type": "string", - "max": 512, - "min": 1, - "sensitive": true - }, - "CreateUploadUrlRequestContentLengthLong": { - "type": "long", - "box": true, - "min": 1 - }, - "CreateUploadUrlRequestContentMd5String": { - "type": "string", - "max": 128, - "min": 1, - "sensitive": true - }, - "CreateUploadUrlResponse": { - "type": "structure", - "required": ["uploadId", "uploadUrl"], - "members": { - "uploadId": { - "shape": "UploadId" - }, - "uploadUrl": { - "shape": "PreSignedUrl" - }, - "kmsKeyArn": { - "shape": "ResourceArn" - }, - "requestHeaders": { - "shape": "RequestHeaders" - } - } - }, - "CreateUserMemoryEntryInput": { - "type": "structure", - "required": ["memoryEntryString", "origin"], - "members": { - "memoryEntryString": { - "shape": "CreateUserMemoryEntryInputMemoryEntryStringString" - }, - "origin": { - "shape": "Origin" - }, - "profileArn": { - "shape": "CreateUserMemoryEntryInputProfileArnString" - }, - "clientToken": { - "shape": "String", - "idempotencyToken": true - } - } - }, - "CreateUserMemoryEntryInputMemoryEntryStringString": { - "type": "string", - "min": 1, - "sensitive": true - }, - "CreateUserMemoryEntryInputProfileArnString": { - "type": "string", - "min": 1, - "pattern": "arn:aws:codewhisperer:[-.a-z0-9]{1,63}:\\d{12}:profile/([a-zA-Z0-9]){12}" - }, - "CreateUserMemoryEntryOutput": { - "type": "structure", - "required": ["memoryEntry"], - "members": { - "memoryEntry": { - "shape": "MemoryEntry" - } - } - }, - "CreateWorkspaceRequest": { - "type": "structure", - "required": ["workspaceRoot"], - "members": { - "workspaceRoot": { - "shape": "CreateWorkspaceRequestWorkspaceRootString" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "CreateWorkspaceRequestWorkspaceRootString": { - "type": "string", - "max": 1024, - "min": 1, - "sensitive": true - }, - "CreateWorkspaceResponse": { - "type": "structure", - "required": ["workspace"], - "members": { - "workspace": { - "shape": "WorkspaceMetadata" - } - } - }, - "CursorState": { - "type": "structure", - "members": { - "position": { - "shape": "Position" - }, - "range": { - "shape": "Range" - } - }, - "union": true - }, - "Customization": { - "type": "structure", - "required": ["arn"], - "members": { - "arn": { - "shape": "CustomizationArn" - }, - "name": { - "shape": "CustomizationName" - }, - "description": { - "shape": "Description" - } - } - }, - "CustomizationArn": { - "type": "string", - "max": 950, - "min": 0, - "pattern": "arn:[-.a-z0-9]{1,63}:codewhisperer:([-.a-z0-9]{0,63}:){2}([a-zA-Z0-9-_:/]){1,1023}" - }, - "CustomizationName": { - "type": "string", - "max": 100, - "min": 1, - "pattern": "[a-zA-Z][a-zA-Z0-9_-]*" - }, - "Customizations": { - "type": "list", - "member": { - "shape": "Customization" - } - }, - "DashboardAnalytics": { - "type": "structure", - "required": ["toggle"], - "members": { - "toggle": { - "shape": "OptInFeatureToggle" - } - } - }, - "DeleteTaskAssistConversationRequest": { - "type": "structure", - "required": ["conversationId"], - "members": { - "conversationId": { - "shape": "ConversationId" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "DeleteTaskAssistConversationResponse": { - "type": "structure", - "required": ["conversationId"], - "members": { - "conversationId": { - "shape": "ConversationId" - } - } - }, - "DeleteUserMemoryEntryInput": { - "type": "structure", - "required": ["id"], - "members": { - "id": { - "shape": "DeleteUserMemoryEntryInputIdString" - }, - "profileArn": { - "shape": "DeleteUserMemoryEntryInputProfileArnString" - } - } - }, - "DeleteUserMemoryEntryInputIdString": { - "type": "string", - "max": 36, - "min": 36, - "pattern": "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}" - }, - "DeleteUserMemoryEntryInputProfileArnString": { - "type": "string", - "min": 1, - "pattern": "arn:aws:codewhisperer:[-.a-z0-9]{1,63}:\\d{12}:profile/([a-zA-Z0-9]){12}" - }, - "DeleteUserMemoryEntryOutput": { - "type": "structure", - "members": {} - }, - "DeleteWorkspaceRequest": { - "type": "structure", - "required": ["workspaceId"], - "members": { - "workspaceId": { - "shape": "UUID" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "DeleteWorkspaceResponse": { - "type": "structure", - "members": {} - }, - "Description": { - "type": "string", - "max": 256, - "min": 0, - "pattern": "[\\sa-zA-Z0-9_-]*" - }, - "Diagnostic": { - "type": "structure", - "members": { - "textDocumentDiagnostic": { - "shape": "TextDocumentDiagnostic" - }, - "runtimeDiagnostic": { - "shape": "RuntimeDiagnostic" - } - }, - "union": true - }, - "DiagnosticLocation": { - "type": "structure", - "required": ["uri", "range"], - "members": { - "uri": { - "shape": "DiagnosticLocationUriString" - }, - "range": { - "shape": "Range" - } - } - }, - "DiagnosticLocationUriString": { - "type": "string", - "max": 1024, - "min": 1, - "sensitive": true - }, - "DiagnosticRelatedInformation": { - "type": "structure", - "required": ["location", "message"], - "members": { - "location": { - "shape": "DiagnosticLocation" - }, - "message": { - "shape": "DiagnosticRelatedInformationMessageString" - } - } - }, - "DiagnosticRelatedInformationList": { - "type": "list", - "member": { - "shape": "DiagnosticRelatedInformation" - }, - "max": 1024, - "min": 0 - }, - "DiagnosticRelatedInformationMessageString": { - "type": "string", - "max": 1024, - "min": 0, - "sensitive": true - }, - "DiagnosticSeverity": { - "type": "string", - "enum": ["ERROR", "WARNING", "INFORMATION", "HINT"] - }, - "DiagnosticTag": { - "type": "string", - "enum": ["UNNECESSARY", "DEPRECATED"] - }, - "DiagnosticTagList": { - "type": "list", - "member": { - "shape": "DiagnosticTag" - }, - "max": 1024, - "min": 0 - }, - "Dimension": { - "type": "structure", - "members": { - "name": { - "shape": "DimensionNameString" - }, - "value": { - "shape": "DimensionValueString" - } - } - }, - "DimensionList": { - "type": "list", - "member": { - "shape": "Dimension" - }, - "max": 30, - "min": 0 - }, - "DimensionNameString": { - "type": "string", - "max": 255, - "min": 1, - "pattern": "[-a-zA-Z0-9._]*" - }, - "DimensionValueString": { - "type": "string", - "max": 1024, - "min": 1, - "pattern": "[-a-zA-Z0-9._]*" - }, - "DocFolderLevel": { - "type": "string", - "enum": ["SUB_FOLDER", "ENTIRE_WORKSPACE"] - }, - "DocGenerationEvent": { - "type": "structure", - "required": ["conversationId"], - "members": { - "conversationId": { - "shape": "ConversationId" - }, - "numberOfAddChars": { - "shape": "PrimitiveInteger" - }, - "numberOfAddLines": { - "shape": "PrimitiveInteger" - }, - "numberOfAddFiles": { - "shape": "PrimitiveInteger" - }, - "userDecision": { - "shape": "DocUserDecision" - }, - "interactionType": { - "shape": "DocInteractionType" - }, - "userIdentity": { - "shape": "String" - }, - "numberOfNavigation": { - "shape": "PrimitiveInteger" - }, - "folderLevel": { - "shape": "DocFolderLevel" - } - } - }, - "DocInteractionType": { - "type": "string", - "enum": ["GENERATE_README", "UPDATE_README", "EDIT_README"] - }, - "DocUserDecision": { - "type": "string", - "enum": ["ACCEPT", "REJECT"] - }, - "DocV2AcceptanceEvent": { - "type": "structure", - "required": [ - "conversationId", - "numberOfAddedChars", - "numberOfAddedLines", - "numberOfAddedFiles", - "userDecision", - "interactionType", - "numberOfNavigations", - "folderLevel" - ], - "members": { - "conversationId": { - "shape": "ConversationId" - }, - "numberOfAddedChars": { - "shape": "DocV2AcceptanceEventNumberOfAddedCharsInteger" - }, - "numberOfAddedLines": { - "shape": "DocV2AcceptanceEventNumberOfAddedLinesInteger" - }, - "numberOfAddedFiles": { - "shape": "DocV2AcceptanceEventNumberOfAddedFilesInteger" - }, - "userDecision": { - "shape": "DocUserDecision" - }, - "interactionType": { - "shape": "DocInteractionType" - }, - "numberOfNavigations": { - "shape": "DocV2AcceptanceEventNumberOfNavigationsInteger" - }, - "folderLevel": { - "shape": "DocFolderLevel" - } - } - }, - "DocV2AcceptanceEventNumberOfAddedCharsInteger": { - "type": "integer", - "min": 0 - }, - "DocV2AcceptanceEventNumberOfAddedFilesInteger": { - "type": "integer", - "min": 0 - }, - "DocV2AcceptanceEventNumberOfAddedLinesInteger": { - "type": "integer", - "min": 0 - }, - "DocV2AcceptanceEventNumberOfNavigationsInteger": { - "type": "integer", - "min": 0 - }, - "DocV2GenerationEvent": { - "type": "structure", - "required": [ - "conversationId", - "numberOfGeneratedChars", - "numberOfGeneratedLines", - "numberOfGeneratedFiles" - ], - "members": { - "conversationId": { - "shape": "ConversationId" - }, - "numberOfGeneratedChars": { - "shape": "DocV2GenerationEventNumberOfGeneratedCharsInteger" - }, - "numberOfGeneratedLines": { - "shape": "DocV2GenerationEventNumberOfGeneratedLinesInteger" - }, - "numberOfGeneratedFiles": { - "shape": "DocV2GenerationEventNumberOfGeneratedFilesInteger" - }, - "interactionType": { - "shape": "DocInteractionType" - }, - "numberOfNavigations": { - "shape": "DocV2GenerationEventNumberOfNavigationsInteger" - }, - "folderLevel": { - "shape": "DocFolderLevel" - } - } - }, - "DocV2GenerationEventNumberOfGeneratedCharsInteger": { - "type": "integer", - "min": 0 - }, - "DocV2GenerationEventNumberOfGeneratedFilesInteger": { - "type": "integer", - "min": 0 - }, - "DocV2GenerationEventNumberOfGeneratedLinesInteger": { - "type": "integer", - "min": 0 - }, - "DocV2GenerationEventNumberOfNavigationsInteger": { - "type": "integer", - "min": 0 - }, - "DocumentSymbol": { - "type": "structure", - "required": ["name", "type"], - "members": { - "name": { - "shape": "DocumentSymbolNameString" - }, - "type": { - "shape": "SymbolType" - }, - "source": { - "shape": "DocumentSymbolSourceString" - } - } - }, - "DocumentSymbolNameString": { - "type": "string", - "max": 256, - "min": 1 - }, - "DocumentSymbolSourceString": { - "type": "string", - "max": 256, - "min": 1 - }, - "DocumentSymbols": { - "type": "list", - "member": { - "shape": "DocumentSymbol" - }, - "max": 1000, - "min": 0 - }, - "DocumentationIntentContext": { - "type": "structure", - "required": ["type"], - "members": { - "scope": { - "shape": "DocumentationIntentContextScopeString" - }, - "type": { - "shape": "DocumentationType" - } - } - }, - "DocumentationIntentContextScopeString": { - "type": "string", - "max": 4096, - "min": 1, - "sensitive": true - }, - "DocumentationType": { - "type": "string", - "enum": ["README"] - }, - "Double": { - "type": "double", - "box": true - }, - "EditorState": { - "type": "structure", - "members": { - "document": { - "shape": "TextDocument" - }, - "cursorState": { - "shape": "CursorState" - }, - "relevantDocuments": { - "shape": "RelevantDocumentList" - }, - "useRelevantDocuments": { - "shape": "Boolean" - }, - "workspaceFolders": { - "shape": "WorkspaceFolderList" - } - } - }, - "EnvState": { - "type": "structure", - "members": { - "operatingSystem": { - "shape": "EnvStateOperatingSystemString" - }, - "currentWorkingDirectory": { - "shape": "EnvStateCurrentWorkingDirectoryString" - }, - "environmentVariables": { - "shape": "EnvironmentVariables" - }, - "timezoneOffset": { - "shape": "EnvStateTimezoneOffsetInteger" - } - } - }, - "EnvStateCurrentWorkingDirectoryString": { - "type": "string", - "max": 256, - "min": 1, - "sensitive": true - }, - "EnvStateOperatingSystemString": { - "type": "string", - "max": 32, - "min": 1, - "pattern": "(macos|linux|windows)" - }, - "EnvStateTimezoneOffsetInteger": { - "type": "integer", - "box": true, - "max": 1440, - "min": -1440 - }, - "EnvironmentVariable": { - "type": "structure", - "members": { - "key": { - "shape": "EnvironmentVariableKeyString" - }, - "value": { - "shape": "EnvironmentVariableValueString" - } - } - }, - "EnvironmentVariableKeyString": { - "type": "string", - "max": 256, - "min": 1, - "sensitive": true - }, - "EnvironmentVariableValueString": { - "type": "string", - "max": 1024, - "min": 1, - "sensitive": true - }, - "EnvironmentVariables": { - "type": "list", - "member": { - "shape": "EnvironmentVariable" - }, - "max": 100, - "min": 0 - }, - "ErrorDetails": { - "type": "string", - "max": 2048, - "min": 0 - }, - "Event": { - "type": "structure", - "required": ["eventId", "generationId", "eventTimestamp", "eventType", "eventBlob"], - "members": { - "eventId": { - "shape": "UUID" - }, - "generationId": { - "shape": "UUID" - }, - "eventTimestamp": { - "shape": "SyntheticTimestamp_date_time" - }, - "eventType": { - "shape": "EventType" - }, - "eventBlob": { - "shape": "EventBlob" - } - } - }, - "EventBlob": { - "type": "blob", - "max": 400000, - "min": 1, - "sensitive": true - }, - "EventList": { - "type": "list", - "member": { - "shape": "Event" - }, - "max": 10, - "min": 1 - }, - "EventType": { - "type": "string", - "max": 100, - "min": 1 - }, - "ExternalIdentityDetails": { - "type": "structure", - "members": { - "issuerUrl": { - "shape": "IssuerUrl" - }, - "clientId": { - "shape": "ClientId" - }, - "scimEndpoint": { - "shape": "String" - } - } - }, - "FeatureDevCodeAcceptanceEvent": { - "type": "structure", - "required": ["conversationId", "linesOfCodeAccepted", "charactersOfCodeAccepted"], - "members": { - "conversationId": { - "shape": "ConversationId" - }, - "linesOfCodeAccepted": { - "shape": "FeatureDevCodeAcceptanceEventLinesOfCodeAcceptedInteger" - }, - "charactersOfCodeAccepted": { - "shape": "FeatureDevCodeAcceptanceEventCharactersOfCodeAcceptedInteger" - }, - "programmingLanguage": { - "shape": "ProgrammingLanguage" - } - } - }, - "FeatureDevCodeAcceptanceEventCharactersOfCodeAcceptedInteger": { - "type": "integer", - "min": 0 - }, - "FeatureDevCodeAcceptanceEventLinesOfCodeAcceptedInteger": { - "type": "integer", - "min": 0 - }, - "FeatureDevCodeGenerationEvent": { - "type": "structure", - "required": ["conversationId", "linesOfCodeGenerated", "charactersOfCodeGenerated"], - "members": { - "conversationId": { - "shape": "ConversationId" - }, - "linesOfCodeGenerated": { - "shape": "FeatureDevCodeGenerationEventLinesOfCodeGeneratedInteger" - }, - "charactersOfCodeGenerated": { - "shape": "FeatureDevCodeGenerationEventCharactersOfCodeGeneratedInteger" - }, - "programmingLanguage": { - "shape": "ProgrammingLanguage" - } - } - }, - "FeatureDevCodeGenerationEventCharactersOfCodeGeneratedInteger": { - "type": "integer", - "min": 0 - }, - "FeatureDevCodeGenerationEventLinesOfCodeGeneratedInteger": { - "type": "integer", - "min": 0 - }, - "FeatureDevEvent": { - "type": "structure", - "required": ["conversationId"], - "members": { - "conversationId": { - "shape": "ConversationId" - } - } - }, - "FeatureEvaluation": { - "type": "structure", - "required": ["feature", "variation", "value"], - "members": { - "feature": { - "shape": "FeatureName" - }, - "variation": { - "shape": "FeatureVariation" - }, - "value": { - "shape": "FeatureValue" - } - } - }, - "FeatureEvaluationsList": { - "type": "list", - "member": { - "shape": "FeatureEvaluation" - }, - "max": 50, - "min": 0 - }, - "FeatureName": { - "type": "string", - "max": 128, - "min": 1, - "pattern": "[-a-zA-Z0-9._]*" - }, - "FeatureValue": { - "type": "structure", - "members": { - "boolValue": { - "shape": "Boolean" - }, - "doubleValue": { - "shape": "Double" - }, - "longValue": { - "shape": "Long" - }, - "stringValue": { - "shape": "FeatureValueStringType" - } - }, - "union": true - }, - "FeatureValueStringType": { - "type": "string", - "max": 512, - "min": 0 - }, - "FeatureVariation": { - "type": "string", - "max": 128, - "min": 1, - "pattern": "[-a-zA-Z0-9._]*" - }, - "FileContext": { - "type": "structure", - "required": ["leftFileContent", "rightFileContent", "filename", "programmingLanguage"], - "members": { - "leftFileContent": { - "shape": "FileContextLeftFileContentString" - }, - "rightFileContent": { - "shape": "FileContextRightFileContentString" - }, - "filename": { - "shape": "FileContextFilenameString" - }, - "programmingLanguage": { - "shape": "ProgrammingLanguage" - } - } - }, - "FileContextFilenameString": { - "type": "string", - "max": 1024, - "min": 1, - "sensitive": true - }, - "FileContextLeftFileContentString": { - "type": "string", - "max": 10240, - "min": 0, - "sensitive": true - }, - "FileContextRightFileContentString": { - "type": "string", - "max": 10240, - "min": 0, - "sensitive": true - }, - "FollowupPrompt": { - "type": "structure", - "required": ["content"], - "members": { - "content": { - "shape": "FollowupPromptContentString" - }, - "userIntent": { - "shape": "UserIntent" - } - } - }, - "FollowupPromptContentString": { - "type": "string", - "max": 4096, - "min": 0, - "sensitive": true - }, - "FunctionalityName": { - "type": "string", - "enum": [ - "COMPLETIONS", - "ANALYSIS", - "CONVERSATIONS", - "TASK_ASSIST", - "TRANSFORMATIONS", - "CHAT_CUSTOMIZATION", - "TRANSFORMATIONS_WEBAPP", - "FEATURE_DEVELOPMENT" - ], - "max": 64, - "min": 1 - }, - "GenerateCompletionsRequest": { - "type": "structure", - "required": ["fileContext"], - "members": { - "fileContext": { - "shape": "FileContext" - }, - "maxResults": { - "shape": "GenerateCompletionsRequestMaxResultsInteger" - }, - "nextToken": { - "shape": "GenerateCompletionsRequestNextTokenString" - }, - "referenceTrackerConfiguration": { - "shape": "ReferenceTrackerConfiguration" - }, - "supplementalContexts": { - "shape": "SupplementalContextList" - }, - "customizationArn": { - "shape": "CustomizationArn" - }, - "optOutPreference": { - "shape": "OptOutPreference" - }, - "userContext": { - "shape": "UserContext" - }, - "profileArn": { - "shape": "ProfileArn" - }, - "workspaceId": { - "shape": "UUID" - } - } - }, - "GenerateCompletionsRequestMaxResultsInteger": { - "type": "integer", - "box": true, - "max": 10, - "min": 1 - }, - "GenerateCompletionsRequestNextTokenString": { - "type": "string", - "max": 2048, - "min": 0, - "pattern": "(?:[A-Za-z0-9\\+/]{4})*(?:[A-Za-z0-9\\+/]{2}\\=\\=|[A-Za-z0-9\\+/]{3}\\=)?", - "sensitive": true - }, - "GenerateCompletionsResponse": { - "type": "structure", - "members": { - "completions": { - "shape": "Completions" - }, - "nextToken": { - "shape": "SensitiveString" - } - } - }, - "GetCodeAnalysisRequest": { - "type": "structure", - "required": ["jobId"], - "members": { - "jobId": { - "shape": "GetCodeAnalysisRequestJobIdString" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "GetCodeAnalysisRequestJobIdString": { - "type": "string", - "max": 256, - "min": 1 - }, - "GetCodeAnalysisResponse": { - "type": "structure", - "required": ["status"], - "members": { - "status": { - "shape": "CodeAnalysisStatus" - }, - "errorMessage": { - "shape": "SensitiveString" - } - } - }, - "GetCodeFixJobRequest": { - "type": "structure", - "required": ["jobId"], - "members": { - "jobId": { - "shape": "GetCodeFixJobRequestJobIdString" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "GetCodeFixJobRequestJobIdString": { - "type": "string", - "max": 256, - "min": 1, - "pattern": ".*[A-Za-z0-9-:]+.*" - }, - "GetCodeFixJobResponse": { - "type": "structure", - "members": { - "jobStatus": { - "shape": "CodeFixJobStatus" - }, - "suggestedFix": { - "shape": "SuggestedFix" - } - } - }, - "GetTaskAssistCodeGenerationRequest": { - "type": "structure", - "required": ["conversationId", "codeGenerationId"], - "members": { - "conversationId": { - "shape": "ConversationId" - }, - "codeGenerationId": { - "shape": "CodeGenerationId" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "GetTaskAssistCodeGenerationResponse": { - "type": "structure", - "required": ["conversationId", "codeGenerationStatus"], - "members": { - "conversationId": { - "shape": "ConversationId" - }, - "codeGenerationStatus": { - "shape": "CodeGenerationStatus" - }, - "codeGenerationStatusDetail": { - "shape": "CodeGenerationStatusDetail" - }, - "codeGenerationRemainingIterationCount": { - "shape": "Integer" - }, - "codeGenerationTotalIterationCount": { - "shape": "Integer" - } - } - }, - "GetTestGenerationRequest": { - "type": "structure", - "required": ["testGenerationJobGroupName", "testGenerationJobId"], - "members": { - "testGenerationJobGroupName": { - "shape": "TestGenerationJobGroupName" - }, - "testGenerationJobId": { - "shape": "UUID" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "GetTestGenerationResponse": { - "type": "structure", - "members": { - "testGenerationJob": { - "shape": "TestGenerationJob" - } - } - }, - "GetTransformationPlanRequest": { - "type": "structure", - "required": ["transformationJobId"], - "members": { - "transformationJobId": { - "shape": "TransformationJobId" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "GetTransformationPlanResponse": { - "type": "structure", - "required": ["transformationPlan"], - "members": { - "transformationPlan": { - "shape": "TransformationPlan" - } - } - }, - "GetTransformationRequest": { - "type": "structure", - "required": ["transformationJobId"], - "members": { - "transformationJobId": { - "shape": "TransformationJobId" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "GetTransformationResponse": { - "type": "structure", - "required": ["transformationJob"], - "members": { - "transformationJob": { - "shape": "TransformationJob" - } - } - }, - "GitState": { - "type": "structure", - "members": { - "status": { - "shape": "GitStateStatusString" - } - } - }, - "GitStateStatusString": { - "type": "string", - "max": 4096, - "min": 0, - "sensitive": true - }, - "IdeCategory": { - "type": "string", - "enum": ["JETBRAINS", "VSCODE", "CLI", "JUPYTER_MD", "JUPYTER_SM", "ECLIPSE", "VISUAL_STUDIO"], - "max": 64, - "min": 1 - }, - "IdeDiagnostic": { - "type": "structure", - "required": ["ideDiagnosticType"], - "members": { - "range": { - "shape": "Range" - }, - "source": { - "shape": "IdeDiagnosticSourceString" - }, - "severity": { - "shape": "DiagnosticSeverity" - }, - "ideDiagnosticType": { - "shape": "IdeDiagnosticType" - } - } - }, - "IdeDiagnosticList": { - "type": "list", - "member": { - "shape": "IdeDiagnostic" - }, - "max": 1024, - "min": 0 - }, - "IdeDiagnosticSourceString": { - "type": "string", - "max": 1024, - "min": 0, - "sensitive": true - }, - "IdeDiagnosticType": { - "type": "string", - "enum": ["SYNTAX_ERROR", "TYPE_ERROR", "REFERENCE_ERROR", "BEST_PRACTICE", "SECURITY", "OTHER"] - }, - "IdempotencyToken": { - "type": "string", - "max": 256, - "min": 1 - }, - "IdentityDetails": { - "type": "structure", - "members": { - "ssoIdentityDetails": { - "shape": "SSOIdentityDetails" - }, - "externalIdentityDetails": { - "shape": "ExternalIdentityDetails" - } - }, - "union": true - }, - "ImageBlock": { - "type": "structure", - "required": ["format", "source"], - "members": { - "format": { - "shape": "ImageFormat" - }, - "source": { - "shape": "ImageSource" - } - } - }, - "ImageBlocks": { - "type": "list", - "member": { - "shape": "ImageBlock" - }, - "max": 10, - "min": 0 - }, - "ImageFormat": { - "type": "string", - "enum": ["png", "jpeg", "gif", "webp"] - }, - "ImageSource": { - "type": "structure", - "members": { - "bytes": { - "shape": "ImageSourceBytesBlob" - } - }, - "sensitive": true, - "union": true - }, - "ImageSourceBytesBlob": { - "type": "blob", - "max": 1500000, - "min": 1 - }, - "Import": { - "type": "structure", - "members": { - "statement": { - "shape": "ImportStatementString" - } - } - }, - "ImportStatementString": { - "type": "string", - "max": 1024, - "min": 1, - "sensitive": true - }, - "Imports": { - "type": "list", - "member": { - "shape": "Import" - }, - "max": 10, - "min": 0 - }, - "InlineChatEvent": { - "type": "structure", - "required": ["requestId", "timestamp"], - "members": { - "requestId": { - "shape": "UUID" - }, - "timestamp": { - "shape": "Timestamp" - }, - "inputLength": { - "shape": "PrimitiveInteger" - }, - "numSelectedLines": { - "shape": "PrimitiveInteger" - }, - "numSuggestionAddChars": { - "shape": "PrimitiveInteger" - }, - "numSuggestionAddLines": { - "shape": "PrimitiveInteger" - }, - "numSuggestionDelChars": { - "shape": "PrimitiveInteger" - }, - "numSuggestionDelLines": { - "shape": "PrimitiveInteger" - }, - "codeIntent": { - "shape": "Boolean" - }, - "userDecision": { - "shape": "InlineChatUserDecision" - }, - "responseStartLatency": { - "shape": "Double" - }, - "responseEndLatency": { - "shape": "Double" - }, - "programmingLanguage": { - "shape": "ProgrammingLanguage" - } - } - }, - "InlineChatUserDecision": { - "type": "string", - "enum": ["ACCEPT", "REJECT", "DISMISS"] - }, - "Integer": { - "type": "integer", - "box": true - }, - "Intent": { - "type": "string", - "enum": ["DEV", "DOC"] - }, - "IntentContext": { - "type": "structure", - "members": { - "documentation": { - "shape": "DocumentationIntentContext" - } - }, - "union": true - }, - "InternalServerException": { - "type": "structure", - "required": ["message"], - "members": { - "message": { - "shape": "String" - } - }, - "exception": true, - "fault": true, - "retryable": { - "throttling": false - } - }, - "IssuerUrl": { - "type": "string", - "max": 255, - "min": 1 - }, - "LineRangeList": { - "type": "list", - "member": { - "shape": "Range" - } - }, - "ListAvailableCustomizationsRequest": { - "type": "structure", - "members": { - "maxResults": { - "shape": "ListAvailableCustomizationsRequestMaxResultsInteger" - }, - "nextToken": { - "shape": "Base64EncodedPaginationToken" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "ListAvailableCustomizationsRequestMaxResultsInteger": { - "type": "integer", - "box": true, - "max": 100, - "min": 1 - }, - "ListAvailableCustomizationsResponse": { - "type": "structure", - "required": ["customizations"], - "members": { - "customizations": { - "shape": "Customizations" - }, - "nextToken": { - "shape": "Base64EncodedPaginationToken" - } - } - }, - "ListAvailableProfilesRequest": { - "type": "structure", - "members": { - "maxResults": { - "shape": "ListAvailableProfilesRequestMaxResultsInteger" - }, - "nextToken": { - "shape": "Base64EncodedPaginationToken" - } - } - }, - "ListAvailableProfilesRequestMaxResultsInteger": { - "type": "integer", - "box": true, - "max": 10, - "min": 1 - }, - "ListAvailableProfilesResponse": { - "type": "structure", - "required": ["profiles"], - "members": { - "profiles": { - "shape": "ProfileList" - }, - "nextToken": { - "shape": "Base64EncodedPaginationToken" - } - } - }, - "ListCodeAnalysisFindingsRequest": { - "type": "structure", - "required": ["jobId", "codeAnalysisFindingsSchema"], - "members": { - "jobId": { - "shape": "ListCodeAnalysisFindingsRequestJobIdString" - }, - "nextToken": { - "shape": "PaginationToken" - }, - "codeAnalysisFindingsSchema": { - "shape": "CodeAnalysisFindingsSchema" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "ListCodeAnalysisFindingsRequestJobIdString": { - "type": "string", - "max": 256, - "min": 1 - }, - "ListCodeAnalysisFindingsResponse": { - "type": "structure", - "required": ["codeAnalysisFindings"], - "members": { - "nextToken": { - "shape": "PaginationToken" - }, - "codeAnalysisFindings": { - "shape": "SensitiveString" - } - } - }, - "ListEventsRequest": { - "type": "structure", - "required": ["conversationId"], - "members": { - "conversationId": { - "shape": "UUID" - }, - "maxResults": { - "shape": "ListEventsRequestMaxResultsInteger" - }, - "nextToken": { - "shape": "NextToken" - } - } - }, - "ListEventsRequestMaxResultsInteger": { - "type": "integer", - "box": true, - "max": 50, - "min": 1 - }, - "ListEventsResponse": { - "type": "structure", - "required": ["conversationId", "events"], - "members": { - "conversationId": { - "shape": "UUID" - }, - "events": { - "shape": "EventList" - }, - "nextToken": { - "shape": "NextToken" - } - } - }, - "ListFeatureEvaluationsRequest": { - "type": "structure", - "required": ["userContext"], - "members": { - "userContext": { - "shape": "UserContext" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "ListFeatureEvaluationsResponse": { - "type": "structure", - "required": ["featureEvaluations"], - "members": { - "featureEvaluations": { - "shape": "FeatureEvaluationsList" - } - } - }, - "ListUserMemoryEntriesInput": { - "type": "structure", - "members": { - "maxResults": { - "shape": "ListUserMemoryEntriesInputMaxResultsInteger" - }, - "profileArn": { - "shape": "ListUserMemoryEntriesInputProfileArnString" - }, - "nextToken": { - "shape": "ListUserMemoryEntriesInputNextTokenString" - } - } - }, - "ListUserMemoryEntriesInputMaxResultsInteger": { - "type": "integer", - "box": true, - "max": 100, - "min": 1 - }, - "ListUserMemoryEntriesInputNextTokenString": { - "type": "string", - "min": 1 - }, - "ListUserMemoryEntriesInputProfileArnString": { - "type": "string", - "min": 1, - "pattern": "arn:aws:codewhisperer:[-.a-z0-9]{1,63}:\\d{12}:profile/([a-zA-Z0-9]){12}" - }, - "ListUserMemoryEntriesOutput": { - "type": "structure", - "required": ["memoryEntries"], - "members": { - "memoryEntries": { - "shape": "MemoryEntryList" - }, - "nextToken": { - "shape": "ListUserMemoryEntriesOutputNextTokenString" - } - } - }, - "ListUserMemoryEntriesOutputNextTokenString": { - "type": "string", - "min": 1 - }, - "ListWorkspaceMetadataRequest": { - "type": "structure", - "members": { - "workspaceRoot": { - "shape": "ListWorkspaceMetadataRequestWorkspaceRootString" - }, - "nextToken": { - "shape": "String" - }, - "maxResults": { - "shape": "Integer" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "ListWorkspaceMetadataRequestWorkspaceRootString": { - "type": "string", - "max": 1024, - "min": 1, - "sensitive": true - }, - "ListWorkspaceMetadataResponse": { - "type": "structure", - "required": ["workspaces"], - "members": { - "workspaces": { - "shape": "WorkspaceList" - }, - "nextToken": { - "shape": "String" - } - } - }, - "Long": { - "type": "long", - "box": true - }, - "MemoryEntry": { - "type": "structure", - "required": ["id", "memoryEntryString", "metadata"], - "members": { - "id": { - "shape": "MemoryEntryIdString" - }, - "memoryEntryString": { - "shape": "MemoryEntryMemoryEntryStringString" - }, - "metadata": { - "shape": "MemoryEntryMetadata" - } - } - }, - "MemoryEntryIdString": { - "type": "string", - "max": 36, - "min": 36, - "pattern": "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}" - }, - "MemoryEntryList": { - "type": "list", - "member": { - "shape": "MemoryEntry" - } - }, - "MemoryEntryMemoryEntryStringString": { - "type": "string", - "max": 500, - "min": 1, - "sensitive": true - }, - "MemoryEntryMetadata": { - "type": "structure", - "required": ["origin", "createdAt", "updatedAt"], - "members": { - "origin": { - "shape": "Origin" - }, - "attributes": { - "shape": "AttributesMap" - }, - "createdAt": { - "shape": "Timestamp" - }, - "updatedAt": { - "shape": "Timestamp" - } - } - }, - "MessageId": { - "type": "string", - "max": 128, - "min": 0 - }, - "MetricData": { - "type": "structure", - "required": ["metricName", "metricValue", "timestamp", "product"], - "members": { - "metricName": { - "shape": "MetricDataMetricNameString" - }, - "metricValue": { - "shape": "Double" - }, - "timestamp": { - "shape": "Timestamp" - }, - "product": { - "shape": "MetricDataProductString" - }, - "dimensions": { - "shape": "DimensionList" - } - } - }, - "MetricDataMetricNameString": { - "type": "string", - "max": 1024, - "min": 1, - "pattern": "[-a-zA-Z0-9._]*" - }, - "MetricDataProductString": { - "type": "string", - "max": 128, - "min": 1, - "pattern": "[-a-zA-Z0-9._]*" - }, - "NextToken": { - "type": "string", - "max": 1000, - "min": 0 - }, - "Notifications": { - "type": "list", - "member": { - "shape": "NotificationsFeature" - }, - "max": 10, - "min": 0 - }, - "NotificationsFeature": { - "type": "structure", - "required": ["feature", "toggle"], - "members": { - "feature": { - "shape": "FeatureName" - }, - "toggle": { - "shape": "OptInFeatureToggle" - } - } - }, - "OperatingSystem": { - "type": "string", - "enum": ["MAC", "WINDOWS", "LINUX"], - "max": 64, - "min": 1 - }, - "OptInFeatureToggle": { - "type": "string", - "enum": ["ON", "OFF"] - }, - "OptInFeatures": { - "type": "structure", - "members": { - "promptLogging": { - "shape": "PromptLogging" - }, - "byUserAnalytics": { - "shape": "ByUserAnalytics" - }, - "dashboardAnalytics": { - "shape": "DashboardAnalytics" - }, - "notifications": { - "shape": "Notifications" - }, - "workspaceContext": { - "shape": "WorkspaceContext" - } - } - }, - "OptOutPreference": { - "type": "string", - "enum": ["OPTIN", "OPTOUT"] - }, - "Origin": { - "type": "string", - "enum": [ - "CHATBOT", - "CONSOLE", - "DOCUMENTATION", - "MARKETING", - "MOBILE", - "SERVICE_INTERNAL", - "UNIFIED_SEARCH", - "UNKNOWN", - "MD", - "IDE", - "SAGE_MAKER", - "CLI", - "AI_EDITOR", - "OPENSEARCH_DASHBOARD", - "GITLAB" - ] - }, - "PackageInfo": { - "type": "structure", - "members": { - "executionCommand": { - "shape": "SensitiveString" - }, - "buildCommand": { - "shape": "SensitiveString" - }, - "buildOrder": { - "shape": "PackageInfoBuildOrderInteger" - }, - "testFramework": { - "shape": "String" - }, - "packageSummary": { - "shape": "PackageInfoPackageSummaryString" - }, - "packagePlan": { - "shape": "PackageInfoPackagePlanString" - }, - "targetFileInfoList": { - "shape": "TargetFileInfoList" - } - } - }, - "PackageInfoBuildOrderInteger": { - "type": "integer", - "box": true, - "min": 0 - }, - "PackageInfoList": { - "type": "list", - "member": { - "shape": "PackageInfo" - } - }, - "PackageInfoPackagePlanString": { - "type": "string", - "max": 30720, - "min": 0, - "sensitive": true - }, - "PackageInfoPackageSummaryString": { - "type": "string", - "max": 30720, - "min": 0, - "sensitive": true - }, - "PaginationToken": { - "type": "string", - "max": 2048, - "min": 1, - "pattern": "\\S+" - }, - "Position": { - "type": "structure", - "required": ["line", "character"], - "members": { - "line": { - "shape": "Integer" - }, - "character": { - "shape": "Integer" - } - } - }, - "PreSignedUrl": { - "type": "string", - "max": 2048, - "min": 1, - "sensitive": true - }, - "PrimitiveInteger": { - "type": "integer" - }, - "Profile": { - "type": "structure", - "required": ["arn", "profileName"], - "members": { - "arn": { - "shape": "ProfileArn" - }, - "identityDetails": { - "shape": "IdentityDetails" - }, - "profileName": { - "shape": "ProfileName" - }, - "description": { - "shape": "ProfileDescription" - }, - "referenceTrackerConfiguration": { - "shape": "ReferenceTrackerConfiguration" - }, - "kmsKeyArn": { - "shape": "ResourceArn" - }, - "activeFunctionalities": { - "shape": "ActiveFunctionalityList" - }, - "status": { - "shape": "ProfileStatus" - }, - "errorDetails": { - "shape": "ErrorDetails" - }, - "resourcePolicy": { - "shape": "ResourcePolicy" - }, - "profileType": { - "shape": "ProfileType" - }, - "optInFeatures": { - "shape": "OptInFeatures" - }, - "permissionUpdateRequired": { - "shape": "Boolean" - }, - "applicationProperties": { - "shape": "ApplicationPropertiesList" - } - } - }, - "ProfileArn": { - "type": "string", - "max": 950, - "min": 0, - "pattern": "arn:aws:codewhisperer:[-.a-z0-9]{1,63}:\\d{12}:profile/([a-zA-Z0-9]){12}" - }, - "ProfileDescription": { - "type": "string", - "max": 256, - "min": 1, - "pattern": "[\\sa-zA-Z0-9_-]*" - }, - "ProfileList": { - "type": "list", - "member": { - "shape": "Profile" - } - }, - "ProfileName": { - "type": "string", - "max": 100, - "min": 1, - "pattern": "[a-zA-Z][a-zA-Z0-9_-]*" - }, - "ProfileStatus": { - "type": "string", - "enum": ["ACTIVE", "CREATING", "CREATE_FAILED", "UPDATING", "UPDATE_FAILED", "DELETING", "DELETE_FAILED"] - }, - "ProfileType": { - "type": "string", - "enum": ["Q_DEVELOPER", "CODEWHISPERER"] - }, - "ProgrammingLanguage": { - "type": "structure", - "required": ["languageName"], - "members": { - "languageName": { - "shape": "ProgrammingLanguageLanguageNameString" - } - } - }, - "ProgrammingLanguageLanguageNameString": { - "type": "string", - "max": 128, - "min": 1, - "pattern": "(python|javascript|java|csharp|typescript|c|cpp|go|kotlin|php|ruby|rust|scala|shell|sql|json|yaml|vue|tf|tsx|jsx|plaintext|systemverilog|dart|lua|swift|powershell|r)" - }, - "ProgressUpdates": { - "type": "list", - "member": { - "shape": "TransformationProgressUpdate" - } - }, - "PromptLogging": { - "type": "structure", - "required": ["s3Uri", "toggle"], - "members": { - "s3Uri": { - "shape": "S3Uri" - }, - "toggle": { - "shape": "OptInFeatureToggle" - } - } - }, - "Range": { - "type": "structure", - "required": ["start", "end"], - "members": { - "start": { - "shape": "Position" - }, - "end": { - "shape": "Position" - } - } - }, - "RecommendationsWithReferencesPreference": { - "type": "string", - "enum": ["BLOCK", "ALLOW"] - }, - "Reference": { - "type": "structure", - "members": { - "licenseName": { - "shape": "ReferenceLicenseNameString" - }, - "repository": { - "shape": "ReferenceRepositoryString" - }, - "url": { - "shape": "ReferenceUrlString" - }, - "recommendationContentSpan": { - "shape": "Span" - } - } - }, - "ReferenceLicenseNameString": { - "type": "string", - "max": 1024, - "min": 1 - }, - "ReferenceRepositoryString": { - "type": "string", - "max": 1024, - "min": 1 - }, - "ReferenceTrackerConfiguration": { - "type": "structure", - "required": ["recommendationsWithReferences"], - "members": { - "recommendationsWithReferences": { - "shape": "RecommendationsWithReferencesPreference" - } - } - }, - "ReferenceUrlString": { - "type": "string", - "max": 1024, - "min": 1 - }, - "References": { - "type": "list", - "member": { - "shape": "Reference" - }, - "max": 10, - "min": 0 - }, - "RelevantDocumentList": { - "type": "list", - "member": { - "shape": "RelevantTextDocument" - }, - "max": 30, - "min": 0 - }, - "RelevantTextDocument": { - "type": "structure", - "required": ["relativeFilePath"], - "members": { - "relativeFilePath": { - "shape": "RelevantTextDocumentRelativeFilePathString" - }, - "programmingLanguage": { - "shape": "ProgrammingLanguage" - }, - "text": { - "shape": "RelevantTextDocumentTextString" - }, - "documentSymbols": { - "shape": "DocumentSymbols" - } - } - }, - "RelevantTextDocumentRelativeFilePathString": { - "type": "string", - "max": 4096, - "min": 1, - "sensitive": true - }, - "RelevantTextDocumentTextString": { - "type": "string", - "max": 40960, - "min": 0, - "sensitive": true - }, - "RequestHeaderKey": { - "type": "string", - "max": 64, - "min": 1 - }, - "RequestHeaderValue": { - "type": "string", - "max": 256, - "min": 1 - }, - "RequestHeaders": { - "type": "map", - "key": { - "shape": "RequestHeaderKey" - }, - "value": { - "shape": "RequestHeaderValue" - }, - "max": 16, - "min": 1, - "sensitive": true - }, - "ResourceArn": { - "type": "string", - "max": 1224, - "min": 0, - "pattern": "arn:([-.a-z0-9]{1,63}:){2}([-.a-z0-9]{0,63}:){2}([a-zA-Z0-9-_:/]){1,1023}" - }, - "ResourceNotFoundException": { - "type": "structure", - "required": ["message"], - "members": { - "message": { - "shape": "String" - } - }, - "exception": true - }, - "ResourcePolicy": { - "type": "structure", - "required": ["effect"], - "members": { - "effect": { - "shape": "ResourcePolicyEffect" - } - } - }, - "ResourcePolicyEffect": { - "type": "string", - "enum": ["ALLOW", "DENY"] - }, - "ResumeTransformationRequest": { - "type": "structure", - "required": ["transformationJobId"], - "members": { - "transformationJobId": { - "shape": "TransformationJobId" - }, - "userActionStatus": { - "shape": "TransformationUserActionStatus" - }, - "profileArn": { - "shape": "ProfileArn" - } - } - }, - "ResumeTransformationResponse": { - "type": "structure", - "required": ["transformationStatus"], - "members": { - "transformationStatus": { - "shape": "TransformationStatus" - } - } - }, - "RuntimeDiagnostic": { - "type": "structure", - "required": ["source", "severity", "message"], - "members": { - "source": { - "shape": "RuntimeDiagnosticSourceString" - }, - "severity": { - "shape": "DiagnosticSeverity" - }, - "message": { - "shape": "RuntimeDiagnosticMessageString" - } - } - }, - "RuntimeDiagnosticMessageString": { - "type": "string", - "max": 1024, - "min": 0, - "sensitive": true - }, - "RuntimeDiagnosticSourceString": { - "type": "string", - "max": 1024, - "min": 0, - "sensitive": true - }, - "S3Uri": { - "type": "string", - "max": 1024, - "min": 1, - "pattern": "s3://((?!xn--)[a-z0-9](?![^/]*[.]{2})[a-z0-9-.]{1,61}[a-z0-9](?): Promise { - const bearerToken = await AuthUtil.instance.getBearerToken() - const cwsprConfig = getCodewhispererConfig() - return (await globals.sdkClientBuilder.createAwsService( - Service, - { - apiConfig: apiConfig, - region: cwsprConfig.region, - endpoint: cwsprConfig.endpoint, - token: new Token({ token: bearerToken }), - httpOptions: { - connectTimeout: 10000, // 10 seconds, 3 times P99 API latency - }, - ...options, - } as ServiceOptions, - undefined - )) as FeatureDevProxyClient -} - -export class FeatureDevClient implements FeatureClient { - public async getClient(options?: Partial) { - // Should not be stored for the whole session. - // Client has to be reinitialized for each request so we always have a fresh bearerToken - return await createFeatureDevProxyClient(options) - } - - public async createConversation() { - try { - const client = await this.getClient(writeAPIRetryOptions) - getLogger().debug(`Executing createTaskAssistConversation with {}`) - const { conversationId, $response } = await client - .createTaskAssistConversation({ - profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, - }) - .promise() - getLogger().debug(`${featureName}: Created conversation: %O`, { - conversationId, - requestId: $response.requestId, - }) - return conversationId - } catch (e) { - if (isAwsError(e)) { - getLogger().error( - `${featureName}: failed to start conversation: ${e.message} RequestId: ${e.requestId}` - ) - // BE service will throw ServiceQuota if conversation limit is reached. API Front-end will throw Throttling with this message if conversation limit is reached - if ( - e.code === 'ServiceQuotaExceededException' || - (e.code === 'ThrottlingException' && e.message.includes('reached for this month.')) - ) { - throw new MonthlyConversationLimitError(e.message) - } - throw ApiError.of(e.message, 'CreateConversation', e.code, e.statusCode ?? 500) - } - - throw new UnknownApiError(e instanceof Error ? e.message : 'Unknown error', 'CreateConversation') - } - } - - public async createUploadUrl( - conversationId: string, - contentChecksumSha256: string, - contentLength: number, - uploadId: string - ) { - try { - const client = await this.getClient(writeAPIRetryOptions) - const params: CreateUploadUrlRequest = { - uploadContext: { - taskAssistPlanningUploadContext: { - conversationId, - }, - }, - uploadId, - contentChecksum: contentChecksumSha256, - contentChecksumType: 'SHA_256', - artifactType: 'SourceCode', - uploadIntent: 'TASK_ASSIST_PLANNING', - contentLength, - profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, - } - getLogger().debug(`Executing createUploadUrl with %O`, omit(params, 'contentChecksum')) - const response = await client.createUploadUrl(params).promise() - getLogger().debug(`${featureName}: Created upload url: %O`, { - uploadId: uploadId, - requestId: response.$response.requestId, - }) - return response - } catch (e) { - if (isAwsError(e)) { - getLogger().error( - `${featureName}: failed to generate presigned url: ${e.message} RequestId: ${e.requestId}` - ) - if (e.code === 'ValidationException' && e.message.includes('Invalid contentLength')) { - throw new ContentLengthError() - } - throw ApiError.of(e.message, 'CreateUploadUrl', e.code, e.statusCode ?? 500) - } - - throw new UnknownApiError(e instanceof Error ? e.message : 'Unknown error', 'CreateUploadUrl') - } - } - - public async startCodeGeneration( - conversationId: string, - uploadId: string, - message: string, - intent: FeatureDevProxyClient.Intent, - codeGenerationId: string, - currentCodeGenerationId?: string, - intentContext?: FeatureDevProxyClient.IntentContext - ) { - try { - const client = await this.getClient(writeAPIRetryOptions) - const params: StartTaskAssistCodeGenerationRequest = { - codeGenerationId, - conversationState: { - conversationId, - currentMessage: { - userInputMessage: { content: message }, - }, - chatTriggerType: 'MANUAL', - }, - workspaceState: { - uploadId, - programmingLanguage: { languageName: 'javascript' }, - }, - intent, - profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, - } - if (currentCodeGenerationId) { - params.currentCodeGenerationId = currentCodeGenerationId - } - if (intentContext) { - params.intentContext = intentContext - } - getLogger().debug(`Executing startTaskAssistCodeGeneration with %O`, params) - const response = await client.startTaskAssistCodeGeneration(params).promise() - - return response - } catch (e) { - getLogger().error( - `${featureName}: failed to start code generation: ${(e as Error).message} RequestId: ${ - (e as any).requestId - }` - ) - if (isAwsError(e)) { - // API Front-end will throw Throttling if conversation limit is reached. API Front-end monitors StartCodeGeneration for throttling - if (e.code === 'ThrottlingException' && e.message.includes(startTaskAssistLimitReachedMessage)) { - throw new MonthlyConversationLimitError(e.message) - } - // BE service will throw ServiceQuota if code generation iteration limit is reached - else if ( - e.code === 'ServiceQuotaExceededException' || - (e.code === 'ThrottlingException' && - e.message.includes('limit for number of iterations on a code generation')) - ) { - throw new CodeIterationLimitError() - } - throw ApiError.of(e.message, 'StartTaskAssistCodeGeneration', e.code, e.statusCode ?? 500) - } - - throw new UnknownApiError(e instanceof Error ? e.message : 'Unknown error', 'StartTaskAssistCodeGeneration') - } - } - - public async getCodeGeneration(conversationId: string, codeGenerationId: string) { - try { - const client = await this.getClient() - const params: GetTaskAssistCodeGenerationRequest = { - codeGenerationId, - conversationId, - profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, - } - getLogger().debug(`Executing getTaskAssistCodeGeneration with %O`, params) - const response = await client.getTaskAssistCodeGeneration(params).promise() - - return response - } catch (e) { - getLogger().error( - `${featureName}: failed to start get code generation results: ${(e as Error).message} RequestId: ${ - (e as any).requestId - }` - ) - - if (isAwsError(e)) { - throw ApiError.of(e.message, 'GetTaskAssistCodeGeneration', e.code, e.statusCode ?? 500) - } - - throw new UnknownApiError(e instanceof Error ? e.message : 'Unknown error', 'GetTaskAssistCodeGeneration') - } - } - - public async exportResultArchive(conversationId: string) { - const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile - try { - const streamingClient = await createCodeWhispererChatStreamingClient() - const params = { - exportId: conversationId, - exportIntent: 'TASK_ASSIST', - profileArn: profile?.arn, - } satisfies ExportResultArchiveCommandInput - getLogger().debug(`Executing exportResultArchive with %O`, params) - const archiveResponse = await streamingClient.exportResultArchive(params) - const buffer: number[] = [] - if (archiveResponse.body === undefined) { - throw new ApiServiceError( - 'Empty response from CodeWhisperer Streaming service.', - 'ExportResultArchive', - 'EmptyResponse', - 500 - ) - } - for await (const chunk of archiveResponse.body) { - if (chunk.internalServerException !== undefined) { - throw chunk.internalServerException - } - buffer.push(...(chunk.binaryPayloadEvent?.bytes ?? [])) - } - - const { - code_generation_result: { - new_file_contents: newFiles = {}, - deleted_files: deletedFiles = [], - references = [], - }, - } = JSON.parse(new TextDecoder().decode(Buffer.from(buffer))) as { - // eslint-disable-next-line @typescript-eslint/naming-convention - code_generation_result: { - // eslint-disable-next-line @typescript-eslint/naming-convention - new_file_contents?: Record - // eslint-disable-next-line @typescript-eslint/naming-convention - deleted_files?: string[] - references?: CodeReference[] - } - } - UserWrittenCodeTracker.instance.onQFeatureInvoked() - - const newFileContents: { zipFilePath: string; fileContent: string }[] = [] - for (const [filePath, fileContent] of Object.entries(newFiles)) { - newFileContents.push({ zipFilePath: filePath, fileContent }) - } - - return { newFileContents, deletedFiles, references } - } catch (e) { - getLogger().error( - `${featureName}: failed to export archive result: ${(e as Error).message} RequestId: ${ - (e as any).requestId - }` - ) - - if (isAwsError(e)) { - throw ApiError.of(e.message, 'ExportResultArchive', e.code, e.statusCode ?? 500) - } - - throw new FeatureDevServiceError(e instanceof Error ? e.message : 'Unknown error', 'ExportResultArchive') - } - } - - /** - * This event is specific to ABTesting purposes. - * - * No need to fail currently if the event fails in the request. In addition, currently there is no need for a return value. - * - * @param conversationId - */ - public async sendFeatureDevTelemetryEvent(conversationId: string) { - await this.sendFeatureDevEvent('featureDevEvent', { - conversationId, - }) - } - - public async sendFeatureDevCodeGenerationEvent(event: FeatureDevCodeGenerationEvent) { - getLogger().debug( - `featureDevCodeGenerationEvent: conversationId: ${event.conversationId} charactersOfCodeGenerated: ${event.charactersOfCodeGenerated} linesOfCodeGenerated: ${event.linesOfCodeGenerated}` - ) - await this.sendFeatureDevEvent('featureDevCodeGenerationEvent', event) - } - - public async sendFeatureDevCodeAcceptanceEvent(event: FeatureDevCodeAcceptanceEvent) { - getLogger().debug( - `featureDevCodeAcceptanceEvent: conversationId: ${event.conversationId} charactersOfCodeAccepted: ${event.charactersOfCodeAccepted} linesOfCodeAccepted: ${event.linesOfCodeAccepted}` - ) - await this.sendFeatureDevEvent('featureDevCodeAcceptanceEvent', event) - } - - public async sendMetricData(event: MetricData) { - getLogger().debug(`featureDevCodeGenerationMetricData: dimensions: ${event.dimensions}`) - await this.sendFeatureDevEvent('metricData', event) - } - - public async sendFeatureDevEvent( - eventName: T, - event: NonNullable - ) { - try { - const client = await this.getClient() - const params: FeatureDevProxyClient.SendTelemetryEventRequest = { - telemetryEvent: { - [eventName]: event, - }, - optOutPreference: getOptOutPreference(), - userContext: { - ideCategory: 'VSCODE', - operatingSystem: getOperatingSystem(), - product: 'FeatureDev', // Should be the same as in JetBrains - clientId: getClientId(globals.globalState), - ideVersion: extensionVersion, - }, - profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, - } - const response = await client.sendTelemetryEvent(params).promise() - getLogger().debug( - `${featureName}: successfully sent ${eventName} telemetryEvent:${'conversationId' in event ? ' ConversationId: ' + event.conversationId : ''} RequestId: ${response.$response.requestId}` - ) - } catch (e) { - getLogger().error( - `${featureName}: failed to send ${eventName} telemetry: ${(e as Error).name}: ${ - (e as Error).message - } RequestId: ${(e as any).requestId}` - ) - } - } -} diff --git a/packages/core/src/amazonqFeatureDev/constants.ts b/packages/core/src/amazonqFeatureDev/constants.ts deleted file mode 100644 index 78cae972cc3..00000000000 --- a/packages/core/src/amazonqFeatureDev/constants.ts +++ /dev/null @@ -1,33 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { CodeReference } from '../amazonq/webview/ui/connector' -import { LicenseUtil } from '../codewhisperer/util/licenseUtil' - -// The Scheme name of the virtual documents. -export const featureDevScheme = 'aws-featureDev' - -// For uniquely identifiying which chat messages should be routed to FeatureDev -export const featureDevChat = 'featureDevChat' - -export const featureName = 'Amazon Q Developer Agent for software development' - -export const generateDevFilePrompt = - "generate a devfile in my repository. Note that you should only use devfile version 2.0.0 and the only supported commands are install, build and test (are all optional). so you may have to bundle some commands together using '&&'. also you can use ”public.ecr.aws/aws-mde/universal-image:latest” as universal image if you aren’t sure which image to use. here is an example for a node repository (but don't assume it's always a node project. look at the existing repository structure before generating the devfile): schemaVersion: 2.0.0 components: - name: dev container: image: public.ecr.aws/aws-mde/universal-image:latest commands: - id: install exec: component: dev commandLine: ”npm install” - id: build exec: component: dev commandLine: ”npm run build” - id: test exec: component: dev commandLine: ”npm run test”" - -// Max allowed size for file collection -export const maxRepoSizeBytes = 200 * 1024 * 1024 - -export const startCodeGenClientErrorMessages = ['Improperly formed request', 'Resource not found'] -export const startTaskAssistLimitReachedMessage = 'StartTaskAssistCodeGeneration reached for this month.' -export const clientErrorMessages = [ - 'The folder you chose did not contain any source files in a supported language. Choose another folder and try again.', -] - -// License text that's used in the file view -export const licenseText = (reference: CodeReference) => - `${ - reference.licenseName - } license from repository ${reference.repository}` diff --git a/packages/core/src/amazonqFeatureDev/controllers/chat/controller.ts b/packages/core/src/amazonqFeatureDev/controllers/chat/controller.ts deleted file mode 100644 index bdf73eada07..00000000000 --- a/packages/core/src/amazonqFeatureDev/controllers/chat/controller.ts +++ /dev/null @@ -1,1089 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { ChatItemAction, MynahIcons } from '@aws/mynah-ui' -import * as path from 'path' -import * as vscode from 'vscode' -import { EventEmitter } from 'vscode' -import { telemetry } from '../../../shared/telemetry/telemetry' -import { createSingleFileDialog } from '../../../shared/ui/common/openDialog' -import { - CodeIterationLimitError, - ContentLengthError, - createUserFacingErrorMessage, - denyListedErrors, - FeatureDevServiceError, - getMetricResult, - MonthlyConversationLimitError, - NoChangeRequiredException, - PrepareRepoFailedError, - PromptRefusalException, - SelectedFolderNotInWorkspaceFolderError, - TabIdNotFoundError, - UploadCodeError, - UploadURLExpired, - UserMessageNotFoundError, - WorkspaceFolderNotFoundError, - ZipFileError, -} from '../../errors' -import { codeGenRetryLimit, defaultRetryLimit } from '../../limits' -import { Session } from '../../session/session' -import { featureDevScheme, featureName, generateDevFilePrompt } from '../../constants' -import { - DeletedFileInfo, - DevPhase, - MetricDataOperationName, - MetricDataResult, - type NewFileInfo, -} from '../../../amazonq/commons/types' -import { AuthUtil } from '../../../codewhisperer/util/authUtil' -import { AuthController } from '../../../amazonq/auth/controller' -import { getLogger } from '../../../shared/logger/logger' -import { submitFeedback } from '../../../feedback/vue/submitFeedback' -import { Commands, placeholder } from '../../../shared/vscode/commands2' -import { EditorContentController } from '../../../amazonq/commons/controllers/contentController' -import { openUrl } from '../../../shared/utilities/vsCodeUtils' -import { checkForDevFile, getPathsFromZipFilePath } from '../../../amazonq/util/files' -import { examples, messageWithConversationId } from '../../userFacingText' -import { getWorkspaceFoldersByPrefixes } from '../../../shared/utilities/workspaceUtils' -import { openDeletedDiff, openDiff } from '../../../amazonq/commons/diff' -import { i18n } from '../../../shared/i18n-helper' -import globals from '../../../shared/extensionGlobals' -import { CodeWhispererSettings } from '../../../codewhisperer/util/codewhispererSettings' -import { randomUUID } from '../../../shared/crypto' -import { FollowUpTypes } from '../../../amazonq/commons/types' -import { Messenger } from '../../../amazonq/commons/connector/baseMessenger' -import { BaseChatSessionStorage } from '../../../amazonq/commons/baseChatStorage' - -export interface ChatControllerEventEmitters { - readonly processHumanChatMessage: EventEmitter - readonly followUpClicked: EventEmitter - readonly openDiff: EventEmitter - readonly stopResponse: EventEmitter - readonly tabOpened: EventEmitter - readonly tabClosed: EventEmitter - readonly processChatItemVotedMessage: EventEmitter - readonly processChatItemFeedbackMessage: EventEmitter - readonly authClicked: EventEmitter - readonly processResponseBodyLinkClick: EventEmitter - readonly insertCodeAtPositionClicked: EventEmitter - readonly fileClicked: EventEmitter - readonly storeCodeResultMessageId: EventEmitter -} - -type OpenDiffMessage = { - tabID: string - messageId: string - // currently the zip file path - filePath: string - deleted: boolean - codeGenerationId: string -} - -type fileClickedMessage = { - tabID: string - messageId: string - filePath: string - actionName: string -} - -type StoreMessageIdMessage = { - tabID: string - messageId: string -} - -export class FeatureDevController { - private readonly scheme: string = featureDevScheme - private readonly messenger: Messenger - private readonly sessionStorage: BaseChatSessionStorage - private isAmazonQVisible: boolean - private authController: AuthController - private contentController: EditorContentController - - public constructor( - private readonly chatControllerMessageListeners: ChatControllerEventEmitters, - messenger: Messenger, - sessionStorage: BaseChatSessionStorage, - onDidChangeAmazonQVisibility: vscode.Event - ) { - this.messenger = messenger - this.sessionStorage = sessionStorage - this.authController = new AuthController() - this.contentController = new EditorContentController() - - /** - * defaulted to true because onDidChangeAmazonQVisibility doesn't get fire'd until after - * the view is opened - */ - this.isAmazonQVisible = true - - onDidChangeAmazonQVisibility((visible) => { - this.isAmazonQVisible = visible - }) - - this.chatControllerMessageListeners.processHumanChatMessage.event((data) => { - this.processUserChatMessage(data).catch((e) => { - getLogger().error('processUserChatMessage failed: %s', (e as Error).message) - }) - }) - this.chatControllerMessageListeners.processChatItemVotedMessage.event((data) => { - this.processChatItemVotedMessage(data.tabID, data.vote).catch((e) => { - getLogger().error('processChatItemVotedMessage failed: %s', (e as Error).message) - }) - }) - this.chatControllerMessageListeners.processChatItemFeedbackMessage.event((data) => { - this.processChatItemFeedbackMessage(data).catch((e) => { - getLogger().error('processChatItemFeedbackMessage failed: %s', (e as Error).message) - }) - }) - this.chatControllerMessageListeners.followUpClicked.event((data) => { - switch (data.followUp.type) { - case FollowUpTypes.InsertCode: - return this.insertCode(data) - case FollowUpTypes.ProvideFeedbackAndRegenerateCode: - return this.provideFeedbackAndRegenerateCode(data) - case FollowUpTypes.Retry: - return this.retryRequest(data) - case FollowUpTypes.ModifyDefaultSourceFolder: - return this.modifyDefaultSourceFolder(data) - case FollowUpTypes.DevExamples: - this.initialExamples(data) - break - case FollowUpTypes.NewTask: - this.messenger.sendAnswer({ - type: 'answer', - tabID: data?.tabID, - message: i18n('AWS.amazonq.featureDev.answer.newTaskChanges'), - }) - return this.newTask(data) - case FollowUpTypes.CloseSession: - return this.closeSession(data) - case FollowUpTypes.SendFeedback: - this.sendFeedback() - break - case FollowUpTypes.AcceptAutoBuild: - return this.processAutoBuildSetting(true, data) - case FollowUpTypes.DenyAutoBuild: - return this.processAutoBuildSetting(false, data) - case FollowUpTypes.GenerateDevFile: - this.messenger.sendAnswer({ - type: 'system-prompt', - tabID: data?.tabID, - message: i18n('AWS.amazonq.featureDev.pillText.generateDevFile'), - }) - return this.newTask(data, generateDevFilePrompt) - } - }) - this.chatControllerMessageListeners.openDiff.event((data) => { - return this.openDiff(data) - }) - this.chatControllerMessageListeners.stopResponse.event((data) => { - return this.stopResponse(data) - }) - this.chatControllerMessageListeners.tabOpened.event((data) => { - return this.tabOpened(data) - }) - this.chatControllerMessageListeners.tabClosed.event((data) => { - this.tabClosed(data) - }) - this.chatControllerMessageListeners.authClicked.event((data) => { - this.authClicked(data) - }) - this.chatControllerMessageListeners.processResponseBodyLinkClick.event((data) => { - this.processLink(data) - }) - this.chatControllerMessageListeners.insertCodeAtPositionClicked.event((data) => { - this.insertCodeAtPosition(data) - }) - this.chatControllerMessageListeners.fileClicked.event(async (data) => { - return await this.fileClicked(data) - }) - this.chatControllerMessageListeners.storeCodeResultMessageId.event(async (data) => { - return await this.storeCodeResultMessageId(data) - }) - AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(() => { - this.sessionStorage.deleteAllSessions() - }) - } - - private async processChatItemVotedMessage(tabId: string, vote: string) { - const session = await this.sessionStorage.getSession(tabId) - - if (vote === 'upvote') { - telemetry.amazonq_codeGenerationThumbsUp.emit({ - amazonqConversationId: session?.conversationId, - value: 1, - result: 'Succeeded', - credentialStartUrl: AuthUtil.instance.startUrl, - }) - } else if (vote === 'downvote') { - telemetry.amazonq_codeGenerationThumbsDown.emit({ - amazonqConversationId: session?.conversationId, - value: 1, - result: 'Succeeded', - credentialStartUrl: AuthUtil.instance.startUrl, - }) - } - } - - private async processChatItemFeedbackMessage(message: any) { - const session = await this.sessionStorage.getSession(message.tabId) - - await globals.telemetry.postFeedback({ - comment: `${JSON.stringify({ - type: 'featuredev-chat-answer-feedback', - conversationId: session?.conversationId ?? '', - messageId: message?.messageId, - reason: message?.selectedOption, - userComment: message?.comment, - })}`, - sentiment: 'Negative', // The chat UI reports only negative feedback currently. - }) - } - - private processErrorChatMessage = (err: any, message: any, session: Session | undefined) => { - const errorMessage = createUserFacingErrorMessage( - `${featureName} request failed: ${err.cause?.message ?? err.message}` - ) - - let defaultMessage - const isDenyListedError = denyListedErrors.some((denyListedError) => err.message.includes(denyListedError)) - - switch (err.constructor.name) { - case ContentLengthError.name: - this.messenger.sendAnswer({ - type: 'answer', - tabID: message.tabID, - message: err.message + messageWithConversationId(session?.conversationIdUnsafe), - canBeVoted: true, - }) - this.messenger.sendAnswer({ - type: 'system-prompt', - tabID: message.tabID, - followUps: [ - { - pillText: i18n('AWS.amazonq.featureDev.pillText.modifyDefaultSourceFolder'), - type: 'ModifyDefaultSourceFolder', - status: 'info', - }, - ], - }) - break - case MonthlyConversationLimitError.name: - this.messenger.sendMonthlyLimitError(message.tabID) - break - case FeatureDevServiceError.name: - case UploadCodeError.name: - case UserMessageNotFoundError.name: - case TabIdNotFoundError.name: - case PrepareRepoFailedError.name: - this.messenger.sendErrorMessage( - errorMessage, - message.tabID, - this.retriesRemaining(session), - session?.conversationIdUnsafe - ) - break - case PromptRefusalException.name: - case ZipFileError.name: - this.messenger.sendErrorMessage(errorMessage, message.tabID, 0, session?.conversationIdUnsafe, true) - break - case NoChangeRequiredException.name: - this.messenger.sendAnswer({ - type: 'answer', - tabID: message.tabID, - message: err.message, - canBeVoted: true, - }) - // Allow users to re-work the task description. - return this.newTask(message) - case CodeIterationLimitError.name: - this.messenger.sendAnswer({ - type: 'answer', - tabID: message.tabID, - message: err.message + messageWithConversationId(session?.conversationIdUnsafe), - canBeVoted: true, - }) - this.messenger.sendAnswer({ - type: 'system-prompt', - tabID: message.tabID, - followUps: [ - { - pillText: - session?.getInsertCodePillText([ - ...(session?.state.filePaths ?? []), - ...(session?.state.deletedFiles ?? []), - ]) ?? i18n('AWS.amazonq.featureDev.pillText.acceptAllChanges'), - type: FollowUpTypes.InsertCode, - icon: 'ok' as MynahIcons, - status: 'success', - }, - ], - }) - break - case UploadURLExpired.name: - this.messenger.sendAnswer({ - type: 'answer', - tabID: message.tabID, - message: err.message, - canBeVoted: true, - }) - break - default: - if (isDenyListedError || this.retriesRemaining(session) === 0) { - defaultMessage = i18n('AWS.amazonq.featureDev.error.codeGen.denyListedError') - } else { - defaultMessage = i18n('AWS.amazonq.featureDev.error.codeGen.default') - } - - this.messenger.sendErrorMessage( - defaultMessage ? defaultMessage : errorMessage, - message.tabID, - this.retriesRemaining(session), - session?.conversationIdUnsafe, - !!defaultMessage - ) - - break - } - } - - /** - * - * This function dispose cancellation token to free resources and provide a new token. - * Since user can abort a call in the same session, when the processing ends, we need provide a new one - * to start with the new prompt and allow the ability to stop again. - * - * @param session - */ - - private disposeToken(session: Session | undefined) { - if (session?.state?.tokenSource?.token.isCancellationRequested) { - session?.state.tokenSource?.dispose() - if (session?.state?.tokenSource) { - session.state.tokenSource = new vscode.CancellationTokenSource() - } - getLogger().debug('Request cancelled, skipping further processing') - } - } - - // TODO add type - private async processUserChatMessage(message: any) { - if (message.message === undefined) { - this.messenger.sendErrorMessage('chatMessage should be set', message.tabID, 0, undefined) - return - } - - /** - * Don't attempt to process any chat messages when a workspace folder is not set. - * When the tab is first opened we will throw an error and lock the chat if the workspace - * folder is not found - */ - const workspaceFolders = vscode.workspace.workspaceFolders - if (workspaceFolders === undefined || workspaceFolders.length === 0) { - return - } - - let session - try { - getLogger().debug(`${featureName}: Processing message: ${message.message}`) - - session = await this.sessionStorage.getSession(message.tabID) - // set latestMessage in session as retry would lose context if function returns early - session.latestMessage = message.message - - await session.disableFileList() - const authState = await AuthUtil.instance.getChatAuthState() - if (authState.amazonQ !== 'connected') { - await this.messenger.sendAuthNeededExceptionMessage(authState, message.tabID) - session.isAuthenticating = true - return - } - - const root = session.getWorkspaceRoot() - const autoBuildProjectSetting = CodeWhispererSettings.instance.getAutoBuildSetting() - const hasDevfile = await checkForDevFile(root) - const isPromptedForAutoBuildFeature = Object.keys(autoBuildProjectSetting).includes(root) - - if (hasDevfile && !isPromptedForAutoBuildFeature) { - await this.promptAllowQCommandsConsent(message.tabID) - return - } - - await session.preloader() - - if (session.state.phase === DevPhase.CODEGEN) { - await this.onCodeGeneration(session, message.message, message.tabID) - } - } catch (err: any) { - this.disposeToken(session) - await this.processErrorChatMessage(err, message, session) - // Lock the chat input until they explicitly click one of the follow ups - this.messenger.sendChatInputEnabled(message.tabID, false) - } - } - - private async promptAllowQCommandsConsent(tabID: string) { - this.messenger.sendAnswer({ - tabID: tabID, - message: i18n('AWS.amazonq.featureDev.answer.devFileInRepository'), - type: 'answer', - }) - - this.messenger.sendAnswer({ - message: undefined, - type: 'system-prompt', - followUps: [ - { - pillText: i18n('AWS.amazonq.featureDev.pillText.acceptForProject'), - type: FollowUpTypes.AcceptAutoBuild, - status: 'success', - }, - { - pillText: i18n('AWS.amazonq.featureDev.pillText.declineForProject'), - type: FollowUpTypes.DenyAutoBuild, - status: 'error', - }, - ], - tabID: tabID, - }) - } - - /** - * Handle a regular incoming message when a user is in the code generation phase - */ - private async onCodeGeneration(session: Session, message: string, tabID: string) { - // lock the UI/show loading bubbles - this.messenger.sendAsyncEventProgress( - tabID, - true, - session.retries === codeGenRetryLimit - ? i18n('AWS.amazonq.featureDev.pillText.awaitMessage') - : i18n('AWS.amazonq.featureDev.pillText.awaitMessageRetry') - ) - - try { - this.messenger.sendAnswer({ - message: i18n('AWS.amazonq.featureDev.pillText.requestingChanges'), - type: 'answer-stream', - tabID, - canBeVoted: true, - }) - this.messenger.sendUpdatePlaceholder(tabID, i18n('AWS.amazonq.featureDev.pillText.generatingCode')) - await session.sendMetricDataTelemetry(MetricDataOperationName.StartCodeGeneration, MetricDataResult.Success) - await session.send(message) - const filePaths = session.state.filePaths ?? [] - const deletedFiles = session.state.deletedFiles ?? [] - // Only add the follow up accept/deny buttons when the tab hasn't been closed/request hasn't been cancelled - if (session?.state?.tokenSource?.token.isCancellationRequested) { - return - } - - if (filePaths.length === 0 && deletedFiles.length === 0) { - this.messenger.sendAnswer({ - message: i18n('AWS.amazonq.featureDev.pillText.unableGenerateChanges'), - type: 'answer', - tabID: tabID, - canBeVoted: true, - }) - this.messenger.sendAnswer({ - type: 'system-prompt', - tabID: tabID, - followUps: - this.retriesRemaining(session) > 0 - ? [ - { - pillText: i18n('AWS.amazonq.featureDev.pillText.retry'), - type: FollowUpTypes.Retry, - status: 'warning', - }, - ] - : [], - }) - // Lock the chat input until they explicitly click retry - this.messenger.sendChatInputEnabled(tabID, false) - return - } - - this.messenger.sendCodeResult( - filePaths, - deletedFiles, - session.state.references ?? [], - tabID, - session.uploadId, - session.state.codeGenerationId ?? '' - ) - - const remainingIterations = session.state.codeGenerationRemainingIterationCount - const totalIterations = session.state.codeGenerationTotalIterationCount - - if (remainingIterations !== undefined && totalIterations !== undefined) { - this.messenger.sendAnswer({ - type: 'answer' as const, - tabID: tabID, - message: (() => { - if (remainingIterations > 2) { - return 'Would you like me to add this code to your project, or provide feedback for new code?' - } else if (remainingIterations > 0) { - return `Would you like me to add this code to your project, or provide feedback for new code? You have ${remainingIterations} out of ${totalIterations} code generations left.` - } else { - return 'Would you like me to add this code to your project?' - } - })(), - }) - } - - if (session?.state.phase === DevPhase.CODEGEN) { - const messageId = randomUUID() - session.updateAcceptCodeMessageId(messageId) - session.updateAcceptCodeTelemetrySent(false) - // need to add the followUps with an extra update here, or it will double-render them - this.messenger.sendAnswer({ - message: undefined, - type: 'system-prompt', - followUps: [], - tabID: tabID, - messageId, - }) - await session.updateChatAnswer(tabID, i18n('AWS.amazonq.featureDev.pillText.acceptAllChanges')) - await session.sendLinesOfCodeGeneratedTelemetry() - } - this.messenger.sendUpdatePlaceholder(tabID, i18n('AWS.amazonq.featureDev.pillText.selectOption')) - } catch (err: any) { - getLogger().error(`${featureName}: Error during code generation: ${err}`) - await session.sendMetricDataTelemetry(MetricDataOperationName.EndCodeGeneration, getMetricResult(err)) - throw err - } finally { - // Finish processing the event - - if (session?.state?.tokenSource?.token.isCancellationRequested) { - await this.workOnNewTask( - session.tabID, - session.state.codeGenerationRemainingIterationCount, - session.state.codeGenerationTotalIterationCount, - session?.state?.tokenSource?.token.isCancellationRequested - ) - this.disposeToken(session) - } else { - this.messenger.sendAsyncEventProgress(tabID, false, undefined) - - // Lock the chat input until they explicitly click one of the follow ups - this.messenger.sendChatInputEnabled(tabID, false) - - if (!this.isAmazonQVisible) { - const open = 'Open chat' - const resp = await vscode.window.showInformationMessage( - i18n('AWS.amazonq.featureDev.answer.qGeneratedCode'), - open - ) - if (resp === open) { - await Commands.tryExecute('aws.amazonq.AmazonQChatView.focus') - // TODO add focusing on the specific tab once that's implemented - } - } - } - } - await session.sendMetricDataTelemetry(MetricDataOperationName.EndCodeGeneration, MetricDataResult.Success) - } - - private sendUpdateCodeMessage(tabID: string) { - this.messenger.sendAnswer({ - type: 'answer', - tabID, - message: i18n('AWS.amazonq.featureDev.answer.updateCode'), - canBeVoted: true, - }) - } - - private async workOnNewTask( - tabID: string, - remainingIterations: number = 0, - totalIterations?: number, - isStoppedGeneration: boolean = false - ) { - const hasDevFile = await checkForDevFile((await this.sessionStorage.getSession(tabID)).getWorkspaceRoot()) - - if (isStoppedGeneration) { - this.messenger.sendAnswer({ - message: ((remainingIterations) => { - if (totalIterations !== undefined) { - if (remainingIterations <= 0) { - return "I stopped generating your code. You don't have more iterations left, however, you can start a new session." - } else if (remainingIterations <= 2) { - return `I stopped generating your code. If you want to continue working on this task, provide another description. You have ${remainingIterations} out of ${totalIterations} code generations left.` - } - } - return 'I stopped generating your code. If you want to continue working on this task, provide another description.' - })(remainingIterations), - type: 'answer-part', - tabID, - }) - } - - if ((remainingIterations <= 0 && isStoppedGeneration) || !isStoppedGeneration) { - const followUps: Array = [ - { - pillText: i18n('AWS.amazonq.featureDev.pillText.newTask'), - type: FollowUpTypes.NewTask, - status: 'info', - }, - { - pillText: i18n('AWS.amazonq.featureDev.pillText.closeSession'), - type: FollowUpTypes.CloseSession, - status: 'info', - }, - ] - - if (!hasDevFile) { - followUps.push({ - pillText: i18n('AWS.amazonq.featureDev.pillText.generateDevFile'), - type: FollowUpTypes.GenerateDevFile, - status: 'info', - }) - - this.messenger.sendAnswer({ - type: 'answer', - tabID, - message: i18n('AWS.amazonq.featureDev.answer.devFileSuggestion'), - }) - } - - this.messenger.sendAnswer({ - type: 'system-prompt', - tabID, - followUps, - }) - this.messenger.sendChatInputEnabled(tabID, false) - this.messenger.sendUpdatePlaceholder(tabID, i18n('AWS.amazonq.featureDev.pillText.selectOption')) - return - } - - // Ensure that chat input is enabled so that they can provide additional iterations if they choose - this.messenger.sendChatInputEnabled(tabID, true) - this.messenger.sendUpdatePlaceholder(tabID, i18n('AWS.amazonq.featureDev.placeholder.additionalImprovements')) - } - - private async processAutoBuildSetting(setting: boolean, msg: any) { - const root = (await this.sessionStorage.getSession(msg.tabID)).getWorkspaceRoot() - await CodeWhispererSettings.instance.updateAutoBuildSetting(root, setting) - - this.messenger.sendAnswer({ - message: i18n('AWS.amazonq.featureDev.answer.settingUpdated'), - tabID: msg.tabID, - type: 'answer', - }) - - await this.retryRequest(msg) - } - - // TODO add type - private async insertCode(message: any) { - let session - try { - session = await this.sessionStorage.getSession(message.tabID) - - const acceptedFiles = (paths?: { rejected: boolean }[]) => (paths || []).filter((i) => !i.rejected).length - - const filesAccepted = acceptedFiles(session.state.filePaths) + acceptedFiles(session.state.deletedFiles) - - this.sendAcceptCodeTelemetry(session, filesAccepted) - - await session.insertChanges() - - if (session.acceptCodeMessageId) { - this.sendUpdateCodeMessage(message.tabID) - await this.workOnNewTask( - message.tabID, - session.state.codeGenerationRemainingIterationCount, - session.state.codeGenerationTotalIterationCount - ) - await this.clearAcceptCodeMessageId(message.tabID) - } - } catch (err: any) { - this.messenger.sendErrorMessage( - createUserFacingErrorMessage(`Failed to insert code changes: ${err.message}`), - message.tabID, - this.retriesRemaining(session), - session?.conversationIdUnsafe - ) - } - } - - private async provideFeedbackAndRegenerateCode(message: any) { - const session = await this.sessionStorage.getSession(message.tabID) - telemetry.amazonq_isProvideFeedbackForCodeGen.emit({ - amazonqConversationId: session.conversationId, - enabled: true, - result: 'Succeeded', - credentialStartUrl: AuthUtil.instance.startUrl, - }) - // Unblock the message button - this.messenger.sendAsyncEventProgress(message.tabID, false, undefined) - - this.messenger.sendAnswer({ - type: 'answer', - tabID: message.tabID, - message: i18n('AWS.amazonq.featureDev.answer.howCodeCanBeImproved'), - canBeVoted: true, - }) - - this.messenger.sendUpdatePlaceholder(message.tabID, i18n('AWS.amazonq.featureDev.placeholder.feedback')) - } - - private async retryRequest(message: any) { - let session - try { - this.messenger.sendAsyncEventProgress(message.tabID, true, undefined) - - session = await this.sessionStorage.getSession(message.tabID) - - // Decrease retries before making this request, just in case this one fails as well - session.decreaseRetries() - - // Sending an empty message will re-run the last state with the previous values - await this.processUserChatMessage({ - message: session.latestMessage, - tabID: message.tabID, - }) - } catch (err: any) { - this.messenger.sendErrorMessage( - createUserFacingErrorMessage(`Failed to retry request: ${err.message}`), - message.tabID, - this.retriesRemaining(session), - session?.conversationIdUnsafe - ) - } finally { - // Finish processing the event - this.messenger.sendAsyncEventProgress(message.tabID, false, undefined) - } - } - - private async modifyDefaultSourceFolder(message: any) { - const session = await this.sessionStorage.getSession(message.tabID) - - const uri = await createSingleFileDialog({ - canSelectFolders: true, - canSelectFiles: false, - }).prompt() - - let metricData: { result: 'Succeeded' } | { result: 'Failed'; reason: string } | undefined - - if (!(uri instanceof vscode.Uri)) { - this.messenger.sendAnswer({ - tabID: message.tabID, - type: 'system-prompt', - followUps: [ - { - pillText: i18n('AWS.amazonq.featureDev.pillText.selectFiles'), - type: 'ModifyDefaultSourceFolder', - status: 'info', - }, - ], - }) - metricData = { result: 'Failed', reason: 'ClosedBeforeSelection' } - } else if (!vscode.workspace.getWorkspaceFolder(uri)) { - this.messenger.sendAnswer({ - tabID: message.tabID, - type: 'answer', - message: new SelectedFolderNotInWorkspaceFolderError().message, - canBeVoted: true, - }) - this.messenger.sendAnswer({ - tabID: message.tabID, - type: 'system-prompt', - followUps: [ - { - pillText: i18n('AWS.amazonq.featureDev.pillText.selectFiles'), - type: 'ModifyDefaultSourceFolder', - status: 'info', - }, - ], - }) - metricData = { result: 'Failed', reason: 'NotInWorkspaceFolder' } - } else { - session.updateWorkspaceRoot(uri.fsPath) - metricData = { result: 'Succeeded' } - this.messenger.sendAnswer({ - message: `Changed source root to: ${uri.fsPath}`, - type: 'answer', - tabID: message.tabID, - canBeVoted: true, - }) - this.messenger.sendAnswer({ - message: undefined, - type: 'system-prompt', - followUps: [ - { - pillText: i18n('AWS.amazonq.featureDev.pillText.retry'), - type: FollowUpTypes.Retry, - status: 'warning', - }, - ], - tabID: message.tabID, - }) - this.messenger.sendChatInputEnabled(message.tabID, true) - this.messenger.sendUpdatePlaceholder(message.tabID, i18n('AWS.amazonq.featureDev.pillText.writeNewPrompt')) - } - - telemetry.amazonq_modifySourceFolder.emit({ - credentialStartUrl: AuthUtil.instance.startUrl, - amazonqConversationId: session.conversationId, - ...metricData, - }) - } - - private initialExamples(message: any) { - this.messenger.sendAnswer({ - type: 'answer', - tabID: message.tabID, - message: examples, - canBeVoted: true, - }) - } - - private async fileClicked(message: fileClickedMessage) { - // TODO: add Telemetry here - const tabId: string = message.tabID - const messageId = message.messageId - const filePathToUpdate: string = message.filePath - const action = message.actionName - - const session = await this.sessionStorage.getSession(tabId) - const filePathIndex = (session.state.filePaths ?? []).findIndex((obj) => obj.relativePath === filePathToUpdate) - const deletedFilePathIndex = (session.state.deletedFiles ?? []).findIndex( - (obj) => obj.relativePath === filePathToUpdate - ) - - if (filePathIndex !== -1 && session.state.filePaths) { - if (action === 'accept-change') { - this.sendAcceptCodeTelemetry(session, 1) - await session.insertNewFiles([session.state.filePaths[filePathIndex]]) - await session.insertCodeReferenceLogs(session.state.references ?? []) - await this.openFile(session.state.filePaths[filePathIndex], tabId) - } else { - session.state.filePaths[filePathIndex].rejected = !session.state.filePaths[filePathIndex].rejected - } - } - if (deletedFilePathIndex !== -1 && session.state.deletedFiles) { - if (action === 'accept-change') { - this.sendAcceptCodeTelemetry(session, 1) - await session.applyDeleteFiles([session.state.deletedFiles[deletedFilePathIndex]]) - await session.insertCodeReferenceLogs(session.state.references ?? []) - } else { - session.state.deletedFiles[deletedFilePathIndex].rejected = - !session.state.deletedFiles[deletedFilePathIndex].rejected - } - } - - await session.updateFilesPaths({ - tabID: tabId, - filePaths: session.state.filePaths ?? [], - deletedFiles: session.state.deletedFiles ?? [], - messageId, - }) - - if (session.acceptCodeMessageId) { - const allFilePathsAccepted = session.state.filePaths?.every( - (filePath: NewFileInfo) => !filePath.rejected && filePath.changeApplied - ) - const allDeletedFilePathsAccepted = session.state.deletedFiles?.every( - (filePath: DeletedFileInfo) => !filePath.rejected && filePath.changeApplied - ) - if (allFilePathsAccepted && allDeletedFilePathsAccepted) { - this.sendUpdateCodeMessage(tabId) - await this.workOnNewTask( - tabId, - session.state.codeGenerationRemainingIterationCount, - session.state.codeGenerationTotalIterationCount - ) - await this.clearAcceptCodeMessageId(tabId) - } - } - } - - private async storeCodeResultMessageId(message: StoreMessageIdMessage) { - const tabId: string = message.tabID - const messageId = message.messageId - const session = await this.sessionStorage.getSession(tabId) - - session.updateCodeResultMessageId(messageId) - } - - private async openDiff(message: OpenDiffMessage) { - const tabId: string = message.tabID - const codeGenerationId: string = message.messageId - const zipFilePath: string = message.filePath - const session = await this.sessionStorage.getSession(tabId) - telemetry.amazonq_isReviewedChanges.emit({ - amazonqConversationId: session.conversationId, - enabled: true, - result: 'Succeeded', - credentialStartUrl: AuthUtil.instance.startUrl, - }) - - const workspacePrefixMapping = getWorkspaceFoldersByPrefixes(session.config.workspaceFolders) - const pathInfos = getPathsFromZipFilePath(zipFilePath, workspacePrefixMapping, session.config.workspaceFolders) - - if (message.deleted) { - const name = path.basename(pathInfos.relativePath) - await openDeletedDiff(pathInfos.absolutePath, name, tabId, this.scheme) - } else { - let uploadId = session.uploadId - if (session?.state?.uploadHistory && session.state.uploadHistory[codeGenerationId]) { - uploadId = session?.state?.uploadHistory[codeGenerationId].uploadId - } - const rightPath = path.join(uploadId, zipFilePath) - await openDiff(pathInfos.absolutePath, rightPath, tabId, this.scheme) - } - } - - private async openFile(filePath: NewFileInfo, tabId: string) { - const leftPath = path.join(filePath.workspaceFolder.uri.fsPath, filePath.relativePath) - const rightPath = filePath.virtualMemoryUri.path - await openDiff(leftPath, rightPath, tabId, this.scheme) - } - - private async stopResponse(message: any) { - telemetry.ui_click.emit({ elementId: 'amazonq_stopCodeGeneration' }) - this.messenger.sendAnswer({ - message: i18n('AWS.amazonq.featureDev.pillText.stoppingCodeGeneration'), - type: 'answer-part', - tabID: message.tabID, - }) - this.messenger.sendUpdatePlaceholder( - message.tabID, - i18n('AWS.amazonq.featureDev.pillText.stoppingCodeGeneration') - ) - this.messenger.sendChatInputEnabled(message.tabID, false) - - const session = await this.sessionStorage.getSession(message.tabID) - if (session.state?.tokenSource) { - session.state?.tokenSource?.cancel() - } - } - - private async tabOpened(message: any) { - let session: Session | undefined - try { - session = await this.sessionStorage.getSession(message.tabID) - getLogger().debug(`${featureName}: Session created with id: ${session.tabID}`) - - const authState = await AuthUtil.instance.getChatAuthState() - if (authState.amazonQ !== 'connected') { - void this.messenger.sendAuthNeededExceptionMessage(authState, message.tabID) - session.isAuthenticating = true - return - } - } catch (err: any) { - if (err instanceof WorkspaceFolderNotFoundError) { - this.messenger.sendAnswer({ - type: 'answer', - tabID: message.tabID, - message: err.message, - }) - this.messenger.sendChatInputEnabled(message.tabID, false) - } else { - this.messenger.sendErrorMessage( - createUserFacingErrorMessage(err.message), - message.tabID, - this.retriesRemaining(session), - session?.conversationIdUnsafe - ) - } - } - } - - private authClicked(message: any) { - this.authController.handleAuth(message.authType) - - this.messenger.sendAnswer({ - type: 'answer', - tabID: message.tabID, - message: i18n('AWS.amazonq.featureDev.pillText.reauthenticate'), - }) - - // Explicitly ensure the user goes through the re-authenticate flow - this.messenger.sendChatInputEnabled(message.tabID, false) - } - - private tabClosed(message: any) { - this.sessionStorage.deleteSession(message.tabID) - } - - private async newTask(message: any, prefilledPrompt?: string) { - // Old session for the tab is ending, delete it so we can create a new one for the message id - const session = await this.sessionStorage.getSession(message.tabID) - await session.disableFileList() - telemetry.amazonq_endChat.emit({ - amazonqConversationId: session.conversationId, - amazonqEndOfTheConversationLatency: performance.now() - session.telemetry.sessionStartTime, - result: 'Succeeded', - }) - this.sessionStorage.deleteSession(message.tabID) - - // Re-run the opening flow, where we check auth + create a session - await this.tabOpened(message) - - if (prefilledPrompt) { - await this.processUserChatMessage({ ...message, message: prefilledPrompt }) - } else { - this.messenger.sendChatInputEnabled(message.tabID, true) - this.messenger.sendUpdatePlaceholder(message.tabID, i18n('AWS.amazonq.featureDev.placeholder.describe')) - } - } - - private async closeSession(message: any) { - this.messenger.sendAnswer({ - type: 'answer', - tabID: message.tabID, - message: i18n('AWS.amazonq.featureDev.answer.sessionClosed'), - }) - this.messenger.sendUpdatePlaceholder(message.tabID, i18n('AWS.amazonq.featureDev.placeholder.sessionClosed')) - this.messenger.sendChatInputEnabled(message.tabID, false) - - const session = await this.sessionStorage.getSession(message.tabID) - await session.disableFileList() - telemetry.amazonq_endChat.emit({ - amazonqConversationId: session.conversationId, - amazonqEndOfTheConversationLatency: performance.now() - session.telemetry.sessionStartTime, - result: 'Succeeded', - }) - } - - private sendFeedback() { - void submitFeedback(placeholder, 'Amazon Q') - } - - private processLink(message: any) { - void openUrl(vscode.Uri.parse(message.link)) - } - - private insertCodeAtPosition(message: any) { - this.contentController.insertTextAtCursorPosition(message.code, () => {}) - } - - private retriesRemaining(session: Session | undefined) { - return session?.retries ?? defaultRetryLimit - } - - private async clearAcceptCodeMessageId(tabID: string) { - const session = await this.sessionStorage.getSession(tabID) - session.updateAcceptCodeMessageId(undefined) - } - - private sendAcceptCodeTelemetry(session: Session, amazonqNumberOfFilesAccepted: number) { - // accepted code telemetry is only to be sent once per iteration of code generation - if (amazonqNumberOfFilesAccepted > 0 && !session.acceptCodeTelemetrySent) { - session.updateAcceptCodeTelemetrySent(true) - telemetry.amazonq_isAcceptedCodeChanges.emit({ - credentialStartUrl: AuthUtil.instance.startUrl, - amazonqConversationId: session.conversationId, - amazonqNumberOfFilesAccepted, - enabled: true, - result: 'Succeeded', - }) - } - } -} diff --git a/packages/core/src/amazonqFeatureDev/controllers/chat/messenger/constants.ts b/packages/core/src/amazonqFeatureDev/controllers/chat/messenger/constants.ts deleted file mode 100644 index 086096b68a2..00000000000 --- a/packages/core/src/amazonqFeatureDev/controllers/chat/messenger/constants.ts +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -export type MessengerTypes = 'answer' | 'answer-part' | 'answer-stream' | 'system-prompt' diff --git a/packages/core/src/amazonqFeatureDev/errors.ts b/packages/core/src/amazonqFeatureDev/errors.ts deleted file mode 100644 index 2eb142f765b..00000000000 --- a/packages/core/src/amazonqFeatureDev/errors.ts +++ /dev/null @@ -1,191 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { featureName, clientErrorMessages, startTaskAssistLimitReachedMessage } from './constants' -import { uploadCodeError } from './userFacingText' -import { i18n } from '../shared/i18n-helper' -import { LlmError } from '../amazonq/errors' -import { MetricDataResult } from '../amazonq/commons/types' -import { - ClientError, - ServiceError, - ContentLengthError as CommonContentLengthError, - ToolkitError, -} from '../shared/errors' - -export class ConversationIdNotFoundError extends ServiceError { - constructor() { - super(i18n('AWS.amazonq.featureDev.error.conversationIdNotFoundError'), { - code: 'ConversationIdNotFound', - }) - } -} - -export class TabIdNotFoundError extends ServiceError { - constructor() { - super(i18n('AWS.amazonq.featureDev.error.tabIdNotFoundError'), { - code: 'TabIdNotFound', - }) - } -} - -export class WorkspaceFolderNotFoundError extends ServiceError { - constructor() { - super(i18n('AWS.amazonq.featureDev.error.workspaceFolderNotFoundError'), { - code: 'WorkspaceFolderNotFound', - }) - } -} - -export class UserMessageNotFoundError extends ServiceError { - constructor() { - super(i18n('AWS.amazonq.featureDev.error.userMessageNotFoundError'), { - code: 'MessageNotFound', - }) - } -} - -export class SelectedFolderNotInWorkspaceFolderError extends ClientError { - constructor() { - super(i18n('AWS.amazonq.featureDev.error.selectedFolderNotInWorkspaceFolderError'), { - code: 'SelectedFolderNotInWorkspaceFolder', - }) - } -} - -export class PromptRefusalException extends ClientError { - constructor() { - super(i18n('AWS.amazonq.featureDev.error.promptRefusalException'), { - code: 'PromptRefusalException', - }) - } -} - -export class NoChangeRequiredException extends ClientError { - constructor() { - super(i18n('AWS.amazonq.featureDev.error.noChangeRequiredException'), { - code: 'NoChangeRequiredException', - }) - } -} - -export class FeatureDevServiceError extends ServiceError { - constructor(message: string, code: string) { - super(message, { code }) - } -} - -export class PrepareRepoFailedError extends ServiceError { - constructor() { - super(i18n('AWS.amazonq.featureDev.error.prepareRepoFailedError'), { - code: 'PrepareRepoFailed', - }) - } -} - -export class UploadCodeError extends ServiceError { - constructor(statusCode: string) { - super(uploadCodeError, { code: `UploadCode-${statusCode}` }) - } -} - -export class UploadURLExpired extends ClientError { - constructor() { - super(i18n('AWS.amazonq.featureDev.error.uploadURLExpired'), { code: 'UploadURLExpired' }) - } -} - -export class IllegalStateTransition extends ServiceError { - constructor() { - super(i18n('AWS.amazonq.featureDev.error.illegalStateTransition'), { code: 'IllegalStateTransition' }) - } -} - -export class IllegalStateError extends ServiceError { - constructor(message: string) { - super(message, { code: 'IllegalStateTransition' }) - } -} - -export class ContentLengthError extends CommonContentLengthError { - constructor() { - super(i18n('AWS.amazonq.featureDev.error.contentLengthError'), { code: ContentLengthError.name }) - } -} - -export class ZipFileError extends ServiceError { - constructor() { - super(i18n('AWS.amazonq.featureDev.error.zipFileError'), { code: ZipFileError.name }) - } -} - -export class CodeIterationLimitError extends ClientError { - constructor() { - super(i18n('AWS.amazonq.featureDev.error.codeIterationLimitError'), { code: CodeIterationLimitError.name }) - } -} - -export class MonthlyConversationLimitError extends ClientError { - constructor(message: string) { - super(message, { code: MonthlyConversationLimitError.name }) - } -} - -export class UnknownApiError extends ServiceError { - constructor(message: string, api: string) { - super(message, { code: `${api}-Unknown` }) - } -} - -export class ApiClientError extends ClientError { - constructor(message: string, api: string, errorName: string, errorCode: number) { - super(message, { code: `${api}-${errorName}-${errorCode}` }) - } -} - -export class ApiServiceError extends ServiceError { - constructor(message: string, api: string, errorName: string, errorCode: number) { - super(message, { code: `${api}-${errorName}-${errorCode}` }) - } -} - -export class ApiError { - static of(message: string, api: string, errorName: string, errorCode: number) { - if (errorCode >= 400 && errorCode < 500) { - return new ApiClientError(message, api, errorName, errorCode) - } - return new ApiServiceError(message, api, errorName, errorCode) - } -} - -export const denyListedErrors: string[] = ['Deserialization error', 'Inaccessible host'] - -export function createUserFacingErrorMessage(message: string) { - if (denyListedErrors.some((err) => message.includes(err))) { - return `${featureName} API request failed` - } - return message -} - -function isAPIClientError(error: { code?: string; message: string }): boolean { - return ( - clientErrorMessages.some((msg: string) => error.message.includes(msg)) || - error.message.includes(startTaskAssistLimitReachedMessage) - ) -} - -export function getMetricResult(error: ToolkitError): MetricDataResult { - if (error instanceof ClientError || isAPIClientError(error)) { - return MetricDataResult.Error - } - if (error instanceof ServiceError) { - return MetricDataResult.Fault - } - if (error instanceof LlmError) { - return MetricDataResult.LlmFailure - } - - return MetricDataResult.Fault -} diff --git a/packages/core/src/amazonqFeatureDev/index.ts b/packages/core/src/amazonqFeatureDev/index.ts deleted file mode 100644 index 55114de0a06..00000000000 --- a/packages/core/src/amazonqFeatureDev/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -export * from './userFacingText' -export * from './errors' -export * from './session/sessionState' -export * from './constants' -export { Session } from './session/session' -export { FeatureDevClient } from './client/featureDev' -export { FeatureDevChatSessionStorage } from './storages/chatSession' -export { TelemetryHelper } from '../amazonq/util/telemetryHelper' -export { prepareRepoData, PrepareRepoDataOptions } from '../amazonq/util/files' -export { ChatControllerEventEmitters, FeatureDevController } from './controllers/chat/controller' diff --git a/packages/core/src/amazonqFeatureDev/limits.ts b/packages/core/src/amazonqFeatureDev/limits.ts deleted file mode 100644 index 04b677aaa0f..00000000000 --- a/packages/core/src/amazonqFeatureDev/limits.ts +++ /dev/null @@ -1,14 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -// Max number of times a user can attempt to retry a codegen request if it fails -export const codeGenRetryLimit = 3 - -// The default retry limit used when the session could not be found -export const defaultRetryLimit = 0 - -// The max size a file that is uploaded can be -// 1024 KB -export const maxFileSizeBytes = 1024000 diff --git a/packages/core/src/amazonqFeatureDev/models.ts b/packages/core/src/amazonqFeatureDev/models.ts deleted file mode 100644 index ad37c01e477..00000000000 --- a/packages/core/src/amazonqFeatureDev/models.ts +++ /dev/null @@ -1,12 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -export interface IManifestFile { - pomArtifactId: string - pomFolderName: string - hilCapability: string - pomGroupId: string - sourcePomVersion: string -} diff --git a/packages/core/src/amazonqFeatureDev/session/session.ts b/packages/core/src/amazonqFeatureDev/session/session.ts deleted file mode 100644 index c1fc81a4701..00000000000 --- a/packages/core/src/amazonqFeatureDev/session/session.ts +++ /dev/null @@ -1,412 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as path from 'path' - -import { ConversationNotStartedState, FeatureDevPrepareCodeGenState } from './sessionState' -import { - type DeletedFileInfo, - type Interaction, - type NewFileInfo, - type SessionState, - type SessionStateConfig, - UpdateFilesPathsParams, -} from '../../amazonq/commons/types' -import { ContentLengthError, ConversationIdNotFoundError, IllegalStateError } from '../errors' -import { featureDevChat, featureDevScheme } from '../constants' -import fs from '../../shared/fs/fs' -import { FeatureDevClient } from '../client/featureDev' -import { codeGenRetryLimit } from '../limits' -import { telemetry } from '../../shared/telemetry/telemetry' -import { TelemetryHelper } from '../../amazonq/util/telemetryHelper' -import { ReferenceLogViewProvider } from '../../codewhisperer/service/referenceLogViewProvider' -import { AuthUtil } from '../../codewhisperer/util/authUtil' -import { getLogger } from '../../shared/logger/logger' -import { logWithConversationId } from '../userFacingText' -import { CodeReference } from '../../amazonq/webview/ui/connector' -import { MynahIcons } from '@aws/mynah-ui' -import { i18n } from '../../shared/i18n-helper' -import { computeDiff } from '../../amazonq/commons/diff' -import { UpdateAnswerMessage } from '../../amazonq/commons/connector/connectorMessages' -import { FollowUpTypes } from '../../amazonq/commons/types' -import { SessionConfig } from '../../amazonq/commons/session/sessionConfigFactory' -import { Messenger } from '../../amazonq/commons/connector/baseMessenger' -import { ContentLengthError as CommonContentLengthError } from '../../shared/errors' -import { referenceLogText } from '../../amazonq/commons/model' - -export class Session { - private _state?: SessionState | Omit - private task: string = '' - private proxyClient: FeatureDevClient - private _conversationId?: string - private codeGenRetries: number - private preloaderFinished = false - private _latestMessage: string = '' - private _telemetry: TelemetryHelper - private _codeResultMessageId: string | undefined = undefined - private _acceptCodeMessageId: string | undefined = undefined - private _acceptCodeTelemetrySent = false - private _reportedCodeChanges: Set - - // Used to keep track of whether or not the current session is currently authenticating/needs authenticating - public isAuthenticating: boolean - - constructor( - public readonly config: SessionConfig, - private messenger: Messenger, - public readonly tabID: string, - initialState: Omit = new ConversationNotStartedState(tabID), - proxyClient: FeatureDevClient = new FeatureDevClient() - ) { - this._state = initialState - this.proxyClient = proxyClient - - this.codeGenRetries = codeGenRetryLimit - - this._telemetry = new TelemetryHelper() - this.isAuthenticating = false - this._reportedCodeChanges = new Set() - } - - /** - * Preload any events that have to run before a chat message can be sent - */ - async preloader() { - if (!this.preloaderFinished) { - await this.setupConversation() - this.preloaderFinished = true - this.messenger.sendAsyncEventProgress(this.tabID, true, undefined) - await this.proxyClient.sendFeatureDevTelemetryEvent(this.conversationId) // send the event only once per conversation. - } - } - - /** - * setupConversation - * - * Starts a conversation with the backend and uploads the repo for the LLMs to be able to use it. - */ - private async setupConversation() { - await telemetry.amazonq_startConversationInvoke.run(async (span) => { - this._conversationId = await this.proxyClient.createConversation() - getLogger().info(logWithConversationId(this.conversationId)) - - span.record({ amazonqConversationId: this._conversationId, credentialStartUrl: AuthUtil.instance.startUrl }) - }) - - this._state = new FeatureDevPrepareCodeGenState( - { - ...this.getSessionStateConfig(), - conversationId: this.conversationId, - uploadId: '', - currentCodeGenerationId: undefined, - }, - [], - [], - [], - this.tabID, - 0 - ) - } - - updateWorkspaceRoot(workspaceRootFolder: string) { - this.config.workspaceRoots = [workspaceRootFolder] - this._state && this._state.updateWorkspaceRoot && this._state.updateWorkspaceRoot(workspaceRootFolder) - } - - getWorkspaceRoot(): string { - return this.config.workspaceRoots[0] - } - - private getSessionStateConfig(): Omit { - return { - workspaceRoots: this.config.workspaceRoots, - workspaceFolders: this.config.workspaceFolders, - proxyClient: this.proxyClient, - conversationId: this.conversationId, - } - } - - async send(msg: string): Promise { - // When the task/"thing to do" hasn't been set yet, we want it to be the incoming message - if (this.task === '' && msg) { - this.task = msg - } - - this._latestMessage = msg - - return this.nextInteraction(msg) - } - - private async nextInteraction(msg: string) { - try { - const resp = await this.state.interact({ - task: this.task, - msg, - fs: this.config.fs, - messenger: this.messenger, - telemetry: this.telemetry, - tokenSource: this.state.tokenSource, - uploadHistory: this.state.uploadHistory, - }) - - if (resp.nextState) { - if (!this.state?.tokenSource?.token.isCancellationRequested) { - this.state?.tokenSource?.cancel() - } - // Move to the next state - this._state = resp.nextState - } - - return resp.interaction - } catch (e) { - if (e instanceof CommonContentLengthError) { - getLogger().debug(`Content length validation failed: ${e.message}`) - throw new ContentLengthError() - } - throw e - } - } - - public async updateFilesPaths(params: UpdateFilesPathsParams) { - const { tabID, filePaths, deletedFiles, messageId, disableFileActions = false } = params - this.messenger.updateFileComponent(tabID, filePaths, deletedFiles, messageId, disableFileActions) - await this.updateChatAnswer(tabID, this.getInsertCodePillText([...filePaths, ...deletedFiles])) - } - - public async updateChatAnswer(tabID: string, insertCodePillText: string) { - if (this._acceptCodeMessageId) { - const answer = new UpdateAnswerMessage( - { - messageId: this._acceptCodeMessageId, - messageType: 'system-prompt', - followUps: [ - { - pillText: insertCodePillText, - type: FollowUpTypes.InsertCode, - icon: 'ok' as MynahIcons, - status: 'success', - }, - { - pillText: i18n('AWS.amazonq.featureDev.pillText.provideFeedback'), - type: FollowUpTypes.ProvideFeedbackAndRegenerateCode, - icon: 'refresh' as MynahIcons, - status: 'info', - }, - ], - }, - tabID, - featureDevChat - ) - this.messenger.updateChatAnswer(answer) - } - } - - public async insertChanges() { - const newFilePaths = - this.state.filePaths?.filter((filePath) => !filePath.rejected && !filePath.changeApplied) ?? [] - await this.insertNewFiles(newFilePaths) - - const deletedFiles = - this.state.deletedFiles?.filter((deletedFile) => !deletedFile.rejected && !deletedFile.changeApplied) ?? [] - await this.applyDeleteFiles(deletedFiles) - - await this.insertCodeReferenceLogs(this.state.references ?? []) - - if (this._codeResultMessageId) { - await this.updateFilesPaths({ - tabID: this.state.tabID, - filePaths: this.state.filePaths ?? [], - deletedFiles: this.state.deletedFiles ?? [], - messageId: this._codeResultMessageId, - }) - } - } - - public async insertNewFiles(newFilePaths: NewFileInfo[]) { - await this.sendLinesOfCodeAcceptedTelemetry(newFilePaths) - for (const filePath of newFilePaths) { - const absolutePath = path.join(filePath.workspaceFolder.uri.fsPath, filePath.relativePath) - - const uri = filePath.virtualMemoryUri - const content = await this.config.fs.readFile(uri) - const decodedContent = new TextDecoder().decode(content) - - await fs.mkdir(path.dirname(absolutePath)) - await fs.writeFile(absolutePath, decodedContent) - filePath.changeApplied = true - } - } - - public async applyDeleteFiles(deletedFiles: DeletedFileInfo[]) { - for (const filePath of deletedFiles) { - const absolutePath = path.join(filePath.workspaceFolder.uri.fsPath, filePath.relativePath) - await fs.delete(absolutePath) - filePath.changeApplied = true - } - } - - public async insertCodeReferenceLogs(codeReferences: CodeReference[]) { - for (const ref of codeReferences) { - ReferenceLogViewProvider.instance.addReferenceLog(referenceLogText(ref)) - } - } - - public async disableFileList() { - if (this._codeResultMessageId === undefined) { - return - } - - await this.updateFilesPaths({ - tabID: this.state.tabID, - filePaths: this.state.filePaths ?? [], - deletedFiles: this.state.deletedFiles ?? [], - messageId: this._codeResultMessageId, - disableFileActions: true, - }) - this._codeResultMessageId = undefined - } - - public updateCodeResultMessageId(messageId?: string) { - this._codeResultMessageId = messageId - } - - public updateAcceptCodeMessageId(messageId?: string) { - this._acceptCodeMessageId = messageId - } - - public updateAcceptCodeTelemetrySent(sent: boolean) { - this._acceptCodeTelemetrySent = sent - } - - public getInsertCodePillText(files: Array) { - if (files.every((file) => file.rejected || file.changeApplied)) { - return i18n('AWS.amazonq.featureDev.pillText.continue') - } - if (files.some((file) => file.rejected || file.changeApplied)) { - return i18n('AWS.amazonq.featureDev.pillText.acceptRemainingChanges') - } - return i18n('AWS.amazonq.featureDev.pillText.acceptAllChanges') - } - - public async computeFilePathDiff(filePath: NewFileInfo) { - const leftPath = `${filePath.workspaceFolder.uri.fsPath}/${filePath.relativePath}` - const rightPath = filePath.virtualMemoryUri.path - const diff = await computeDiff(leftPath, rightPath, this.tabID, featureDevScheme) - return { leftPath, rightPath, ...diff } - } - - public async sendMetricDataTelemetry(operationName: string, result: string) { - await this.proxyClient.sendMetricData({ - metricName: 'Operation', - metricValue: 1, - timestamp: new Date(), - product: 'FeatureDev', - dimensions: [ - { - name: 'operationName', - value: operationName, - }, - { - name: 'result', - value: result, - }, - ], - }) - } - - public async sendLinesOfCodeGeneratedTelemetry() { - let charactersOfCodeGenerated = 0 - let linesOfCodeGenerated = 0 - // deleteFiles are currently not counted because the number of lines added is always 0 - const filePaths = this.state.filePaths ?? [] - for (const filePath of filePaths) { - const { leftPath, changes, charsAdded, linesAdded } = await this.computeFilePathDiff(filePath) - const codeChangeKey = `${leftPath}#@${JSON.stringify(changes)}` - if (this._reportedCodeChanges.has(codeChangeKey)) { - continue - } - charactersOfCodeGenerated += charsAdded - linesOfCodeGenerated += linesAdded - this._reportedCodeChanges.add(codeChangeKey) - } - await this.proxyClient.sendFeatureDevCodeGenerationEvent({ - conversationId: this.conversationId, - charactersOfCodeGenerated, - linesOfCodeGenerated, - }) - } - - public async sendLinesOfCodeAcceptedTelemetry(filePaths: NewFileInfo[]) { - let charactersOfCodeAccepted = 0 - let linesOfCodeAccepted = 0 - for (const filePath of filePaths) { - const { charsAdded, linesAdded } = await this.computeFilePathDiff(filePath) - charactersOfCodeAccepted += charsAdded - linesOfCodeAccepted += linesAdded - } - await this.proxyClient.sendFeatureDevCodeAcceptanceEvent({ - conversationId: this.conversationId, - charactersOfCodeAccepted, - linesOfCodeAccepted, - }) - } - - get state() { - if (!this._state) { - throw new IllegalStateError("State should be initialized before it's read") - } - return this._state - } - - get currentCodeGenerationId() { - return this.state.currentCodeGenerationId - } - - get uploadId() { - if (!('uploadId' in this.state)) { - throw new IllegalStateError("UploadId has to be initialized before it's read") - } - return this.state.uploadId - } - - get retries() { - return this.codeGenRetries - } - - decreaseRetries() { - this.codeGenRetries -= 1 - } - get conversationId() { - if (!this._conversationId) { - throw new ConversationIdNotFoundError() - } - return this._conversationId - } - - // Used for cases where it is not needed to have conversationId - get conversationIdUnsafe() { - return this._conversationId - } - - get latestMessage() { - return this._latestMessage - } - - set latestMessage(msg: string) { - this._latestMessage = msg - } - - get telemetry() { - return this._telemetry - } - - get acceptCodeMessageId() { - return this._acceptCodeMessageId - } - - get acceptCodeTelemetrySent() { - return this._acceptCodeTelemetrySent - } -} diff --git a/packages/core/src/amazonqFeatureDev/session/sessionState.ts b/packages/core/src/amazonqFeatureDev/session/sessionState.ts deleted file mode 100644 index 5879c16493f..00000000000 --- a/packages/core/src/amazonqFeatureDev/session/sessionState.ts +++ /dev/null @@ -1,285 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { MynahIcons } from '@aws/mynah-ui' -import * as path from 'path' -import * as vscode from 'vscode' -import { getLogger } from '../../shared/logger/logger' -import { featureDevScheme } from '../constants' -import { - ApiClientError, - ApiServiceError, - IllegalStateTransition, - NoChangeRequiredException, - PromptRefusalException, -} from '../errors' -import { - DeletedFileInfo, - DevPhase, - Intent, - NewFileInfo, - SessionState, - SessionStateAction, - SessionStateConfig, - SessionStateInteraction, -} from '../../amazonq/commons/types' -import { registerNewFiles } from '../../amazonq/util/files' -import { randomUUID } from '../../shared/crypto' -import { collectFiles } from '../../shared/utilities/workspaceUtils' -import { i18n } from '../../shared/i18n-helper' -import { Messenger } from '../../amazonq/commons/connector/baseMessenger' -import { FollowUpTypes } from '../../amazonq/commons/types' -import { - BaseCodeGenState, - BaseMessenger, - BasePrepareCodeGenState, - CreateNextStateParams, -} from '../../amazonq/session/sessionState' -import { LlmError } from '../../amazonq/errors' - -export class ConversationNotStartedState implements Omit { - public tokenSource: vscode.CancellationTokenSource - public readonly phase = DevPhase.INIT - - constructor(public tabID: string) { - this.tokenSource = new vscode.CancellationTokenSource() - } - - async interact(_action: SessionStateAction): Promise { - throw new IllegalStateTransition() - } -} - -export class MockCodeGenState implements SessionState { - public tokenSource: vscode.CancellationTokenSource - public filePaths: NewFileInfo[] - public deletedFiles: DeletedFileInfo[] - public readonly conversationId: string - public readonly codeGenerationId?: string - public readonly uploadId: string - - constructor( - private config: SessionStateConfig, - public tabID: string - ) { - this.tokenSource = new vscode.CancellationTokenSource() - this.filePaths = [] - this.deletedFiles = [] - this.conversationId = this.config.conversationId - this.uploadId = randomUUID() - } - - async interact(action: SessionStateAction): Promise { - // in a `mockcodegen` state, we should read from the `mock-data` folder and output - // every file retrieved in the same shape the LLM would - try { - const files = await collectFiles( - this.config.workspaceFolders.map((f) => path.join(f.uri.fsPath, './mock-data')), - this.config.workspaceFolders, - { - excludeByGitIgnore: false, - } - ) - const newFileContents = files.map((f) => ({ - zipFilePath: f.zipFilePath, - fileContent: f.fileContent, - })) - this.filePaths = registerNewFiles( - action.fs, - newFileContents, - this.uploadId, - this.config.workspaceFolders, - this.conversationId, - featureDevScheme - ) - this.deletedFiles = [ - { - zipFilePath: 'src/this-file-should-be-deleted.ts', - workspaceFolder: this.config.workspaceFolders[0], - relativePath: 'src/this-file-should-be-deleted.ts', - rejected: false, - changeApplied: false, - }, - ] - action.messenger.sendCodeResult( - this.filePaths, - this.deletedFiles, - [ - { - licenseName: 'MIT', - repository: 'foo', - url: 'foo', - }, - ], - this.tabID, - this.uploadId, - this.codeGenerationId ?? '' - ) - action.messenger.sendAnswer({ - message: undefined, - type: 'system-prompt', - followUps: [ - { - pillText: i18n('AWS.amazonq.featureDev.pillText.acceptAllChanges'), - type: FollowUpTypes.InsertCode, - icon: 'ok' as MynahIcons, - status: 'success', - }, - { - pillText: i18n('AWS.amazonq.featureDev.pillText.provideFeedback'), - type: FollowUpTypes.ProvideFeedbackAndRegenerateCode, - icon: 'refresh' as MynahIcons, - status: 'info', - }, - ], - tabID: this.tabID, - }) - } catch (e) { - // TODO: handle this error properly, double check what would be expected behaviour if mock code does not work. - getLogger().error('Unable to use mock code generation: %O', e) - } - - return { - // no point in iterating after a mocked code gen? - nextState: this, - interaction: {}, - } - } -} - -export class FeatureDevCodeGenState extends BaseCodeGenState { - protected handleProgress(messenger: Messenger, action: SessionStateAction, detail?: string): void { - if (detail) { - messenger.sendAnswer({ - message: i18n('AWS.amazonq.featureDev.pillText.generatingCode') + `\n\n${detail}`, - type: 'answer-part', - tabID: this.tabID, - }) - } - } - - protected getScheme(): string { - return featureDevScheme - } - - protected getTimeoutErrorCode(): string { - return 'CodeGenTimeout' - } - - protected handleGenerationComplete( - _messenger: Messenger, - _newFileInfo: NewFileInfo[], - action: SessionStateAction - ): void { - // No special handling needed for feature dev - } - - protected handleError(messenger: BaseMessenger, codegenResult: any): Error { - switch (true) { - case codegenResult.codeGenerationStatusDetail?.includes('Guardrails'): { - return new ApiClientError( - i18n('AWS.amazonq.featureDev.error.codeGen.default'), - 'GetTaskAssistCodeGeneration', - 'GuardrailsException', - 400 - ) - } - case codegenResult.codeGenerationStatusDetail?.includes('PromptRefusal'): { - return new PromptRefusalException() - } - case codegenResult.codeGenerationStatusDetail?.includes('EmptyPatch'): { - if (codegenResult.codeGenerationStatusDetail?.includes('NO_CHANGE_REQUIRED')) { - return new NoChangeRequiredException() - } - return new LlmError(i18n('AWS.amazonq.featureDev.error.codeGen.default'), { - code: 'EmptyPatchException', - }) - } - case codegenResult.codeGenerationStatusDetail?.includes('Throttling'): { - return new ApiClientError( - i18n('AWS.amazonq.featureDev.error.throttling'), - 'GetTaskAssistCodeGeneration', - 'ThrottlingException', - 429 - ) - } - case codegenResult.codeGenerationStatusDetail?.includes('FileCreationFailed'): { - return new ApiServiceError( - i18n('AWS.amazonq.featureDev.error.codeGen.default'), - 'GetTaskAssistCodeGeneration', - 'FileCreationFailedException', - 500 - ) - } - default: { - return new ApiServiceError( - i18n('AWS.amazonq.featureDev.error.codeGen.default'), - 'GetTaskAssistCodeGeneration', - 'UnknownException', - 500 - ) - } - } - } - - protected async startCodeGeneration(action: SessionStateAction, codeGenerationId: string): Promise { - await this.config.proxyClient.startCodeGeneration( - this.config.conversationId, - this.config.uploadId, - action.msg, - Intent.DEV, - codeGenerationId, - this.currentCodeGenerationId - ) - - if (!this.isCancellationRequested) { - action.messenger.sendAnswer({ - message: i18n('AWS.amazonq.featureDev.pillText.generatingCode'), - type: 'answer-part', - tabID: this.tabID, - }) - action.messenger.sendUpdatePlaceholder(this.tabID, i18n('AWS.amazonq.featureDev.pillText.generatingCode')) - } - } - - protected override createNextState(config: SessionStateConfig, params: CreateNextStateParams): SessionState { - return super.createNextState( - { ...config, currentCodeGenerationId: this.currentCodeGenerationId }, - params, - FeatureDevPrepareCodeGenState - ) - } -} - -export class FeatureDevPrepareCodeGenState extends BasePrepareCodeGenState { - protected preUpload(action: SessionStateAction): void { - action.messenger.sendAnswer({ - message: i18n('AWS.amazonq.featureDev.pillText.uploadingCode'), - type: 'answer-part', - tabID: this.tabID, - }) - - action.messenger.sendUpdatePlaceholder(this.tabID, i18n('AWS.amazonq.featureDev.pillText.uploadingCode')) - } - - protected postUpload(action: SessionStateAction): void { - if (!action.tokenSource?.token.isCancellationRequested) { - action.messenger.sendAnswer({ - message: i18n('AWS.amazonq.featureDev.pillText.contextGatheringCompleted'), - type: 'answer-part', - tabID: this.tabID, - }) - - action.messenger.sendUpdatePlaceholder( - this.tabID, - i18n('AWS.amazonq.featureDev.pillText.contextGatheringCompleted') - ) - } - } - - protected override createNextState(config: SessionStateConfig): SessionState { - return super.createNextState(config, FeatureDevCodeGenState) - } -} diff --git a/packages/core/src/amazonqFeatureDev/storages/chatSession.ts b/packages/core/src/amazonqFeatureDev/storages/chatSession.ts deleted file mode 100644 index f45576aa9df..00000000000 --- a/packages/core/src/amazonqFeatureDev/storages/chatSession.ts +++ /dev/null @@ -1,23 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { BaseChatSessionStorage } from '../../amazonq/commons/baseChatStorage' -import { Messenger } from '../../amazonq/commons/connector/baseMessenger' -import { createSessionConfig } from '../../amazonq/commons/session/sessionConfigFactory' -import { featureDevScheme } from '../constants' -import { Session } from '../session/session' - -export class FeatureDevChatSessionStorage extends BaseChatSessionStorage { - constructor(protected readonly messenger: Messenger) { - super() - } - - override async createSession(tabID: string): Promise { - const sessionConfig = await createSessionConfig(featureDevScheme) - const session = new Session(sessionConfig, this.messenger, tabID) - this.sessions.set(tabID, session) - return session - } -} diff --git a/packages/core/src/amazonqFeatureDev/userFacingText.ts b/packages/core/src/amazonqFeatureDev/userFacingText.ts deleted file mode 100644 index 9b8a781ef1a..00000000000 --- a/packages/core/src/amazonqFeatureDev/userFacingText.ts +++ /dev/null @@ -1,25 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { manageAccessGuideURL } from '../amazonq/webview/ui/texts/constants' -import { userGuideURL } from '../amazonq/webview/ui/texts/constants' -import { featureName } from './constants' - -export const examples = ` -You can use /dev to: -- Add a new feature or logic -- Write tests -- Fix a bug in your project -- Generate a README for a file, folder, or project - -To learn more, visit the _[Amazon Q Developer User Guide](${userGuideURL})_. -` - -export const uploadCodeError = `I'm sorry, I couldn't upload your workspace artifacts to Amazon S3 to help you with this task. You might need to allow access to the S3 bucket. For more information, see the [Amazon Q documentation](${manageAccessGuideURL}) or contact your network or organization administrator.` - -// Utils for logging and showing customer facing conversation id text -export const messageWithConversationId = (conversationId?: string) => - conversationId ? `\n\nConversation ID: **${conversationId}**` : '' -export const logWithConversationId = (conversationId: string) => `${featureName} Conversation ID: ${conversationId}` diff --git a/packages/core/src/amazonqFeatureDev/views/actions/uiMessageListener.ts b/packages/core/src/amazonqFeatureDev/views/actions/uiMessageListener.ts deleted file mode 100644 index 5d92fb7188c..00000000000 --- a/packages/core/src/amazonqFeatureDev/views/actions/uiMessageListener.ts +++ /dev/null @@ -1,169 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { ChatControllerEventEmitters } from '../../controllers/chat/controller' -import { MessageListener } from '../../../amazonq/messages/messageListener' -import { ExtensionMessage } from '../../../amazonq/webview/ui/commands' - -export interface UIMessageListenerProps { - readonly chatControllerEventEmitters: ChatControllerEventEmitters - readonly webViewMessageListener: MessageListener -} - -export class UIMessageListener { - private featureDevControllerEventsEmitters: ChatControllerEventEmitters | undefined - private webViewMessageListener: MessageListener - - constructor(props: UIMessageListenerProps) { - this.featureDevControllerEventsEmitters = props.chatControllerEventEmitters - this.webViewMessageListener = props.webViewMessageListener - - // Now we are listening to events that get sent from amazonq/webview/actions/actionListener (e.g. the tab) - this.webViewMessageListener.onMessage((msg) => { - this.handleMessage(msg) - }) - } - - private handleMessage(msg: ExtensionMessage) { - switch (msg.command) { - case 'chat-prompt': - this.processChatMessage(msg) - break - case 'follow-up-was-clicked': - this.followUpClicked(msg) - break - case 'open-diff': - this.openDiff(msg) - break - case 'chat-item-voted': - this.chatItemVoted(msg) - break - case 'chat-item-feedback': - this.chatItemFeedback(msg) - break - case 'stop-response': - this.stopResponse(msg) - break - case 'new-tab-was-created': - this.tabOpened(msg) - break - case 'tab-was-removed': - this.tabClosed(msg) - break - case 'auth-follow-up-was-clicked': - this.authClicked(msg) - break - case 'response-body-link-click': - this.processResponseBodyLinkClick(msg) - break - case 'insert_code_at_cursor_position': - this.insertCodeAtPosition(msg) - break - case 'file-click': - this.fileClicked(msg) - break - case 'store-code-result-message-id': - this.storeCodeResultMessageId(msg) - break - } - } - - private chatItemVoted(msg: any) { - this.featureDevControllerEventsEmitters?.processChatItemVotedMessage.fire({ - tabID: msg.tabID, - command: msg.command, - vote: msg.vote, - messageId: msg.messageId, - }) - } - - private chatItemFeedback(msg: any) { - this.featureDevControllerEventsEmitters?.processChatItemFeedbackMessage.fire(msg) - } - - private processChatMessage(msg: any) { - this.featureDevControllerEventsEmitters?.processHumanChatMessage.fire({ - message: msg.chatMessage, - tabID: msg.tabID, - }) - } - - private followUpClicked(msg: any) { - this.featureDevControllerEventsEmitters?.followUpClicked.fire({ - followUp: msg.followUp, - tabID: msg.tabID, - }) - } - - private fileClicked(msg: any) { - this.featureDevControllerEventsEmitters?.fileClicked.fire({ - tabID: msg.tabID, - filePath: msg.filePath, - actionName: msg.actionName, - messageId: msg.messageId, - }) - } - - private openDiff(msg: any) { - this.featureDevControllerEventsEmitters?.openDiff.fire({ - tabID: msg.tabID, - filePath: msg.filePath, - deleted: msg.deleted, - messageId: msg.messageId, - }) - } - - private stopResponse(msg: any) { - this.featureDevControllerEventsEmitters?.stopResponse.fire({ - tabID: msg.tabID, - }) - } - - private tabOpened(msg: any) { - this.featureDevControllerEventsEmitters?.tabOpened.fire({ - tabID: msg.tabID, - }) - } - - private tabClosed(msg: any) { - this.featureDevControllerEventsEmitters?.tabClosed.fire({ - tabID: msg.tabID, - }) - } - - private authClicked(msg: any) { - this.featureDevControllerEventsEmitters?.authClicked.fire({ - tabID: msg.tabID, - authType: msg.authType, - }) - } - - private processResponseBodyLinkClick(msg: any) { - this.featureDevControllerEventsEmitters?.processResponseBodyLinkClick.fire({ - command: msg.command, - messageId: msg.messageId, - tabID: msg.tabID, - link: msg.link, - }) - } - - private insertCodeAtPosition(msg: any) { - this.featureDevControllerEventsEmitters?.insertCodeAtPositionClicked.fire({ - command: msg.command, - messageId: msg.messageId, - tabID: msg.tabID, - code: msg.code, - insertionTargetType: msg.insertionTargetType, - codeReference: msg.codeReference, - }) - } - - private storeCodeResultMessageId(msg: any) { - this.featureDevControllerEventsEmitters?.storeCodeResultMessageId.fire({ - messageId: msg.messageId, - tabID: msg.tabID, - }) - } -} diff --git a/packages/core/src/codewhisperer/service/transformByQ/humanInTheLoopManager.ts b/packages/core/src/codewhisperer/service/transformByQ/humanInTheLoopManager.ts index 63c1bfe3a2f..1646864e066 100644 --- a/packages/core/src/codewhisperer/service/transformByQ/humanInTheLoopManager.ts +++ b/packages/core/src/codewhisperer/service/transformByQ/humanInTheLoopManager.ts @@ -8,12 +8,19 @@ import path from 'path' import { FolderInfo, transformByQState } from '../../models/model' import fs from '../../../shared/fs/fs' import { createPomCopy, replacePomVersion } from './transformFileHandler' -import { IManifestFile } from '../../../amazonqFeatureDev/models' import { getLogger } from '../../../shared/logger/logger' import { telemetry } from '../../../shared/telemetry/telemetry' import { CodeTransformTelemetryState } from '../../../amazonqGumby/telemetry/codeTransformTelemetryState' import { MetadataResult } from '../../../shared/telemetry/telemetryClient' +export interface IManifestFile { + pomArtifactId: string + pomFolderName: string + hilCapability: string + pomGroupId: string + sourcePomVersion: string +} + /** * @description This class helps encapsulate the "human in the loop" behavior of Amazon Q transform. Users * will be prompted for input during the transformation process. Amazon Q will make some temporary folders diff --git a/packages/core/src/codewhisperer/service/transformByQ/transformFileHandler.ts b/packages/core/src/codewhisperer/service/transformByQ/transformFileHandler.ts index 88f34a799d1..2ec6fdb7c37 100644 --- a/packages/core/src/codewhisperer/service/transformByQ/transformFileHandler.ts +++ b/packages/core/src/codewhisperer/service/transformByQ/transformFileHandler.ts @@ -10,13 +10,13 @@ import xml2js = require('xml2js') import * as CodeWhispererConstants from '../../models/constants' import { existsSync, readFileSync, writeFileSync } from 'fs' // eslint-disable-line no-restricted-imports import { BuildSystem, DB, FolderInfo, transformByQState } from '../../models/model' -import { IManifestFile } from '../../../amazonqFeatureDev/models' import fs from '../../../shared/fs/fs' import globals from '../../../shared/extensionGlobals' import { ChatSessionManager } from '../../../amazonqGumby/chat/storages/chatSession' import { AbsolutePathDetectedError } from '../../../amazonqGumby/errors' import { getLogger } from '../../../shared/logger/logger' import AdmZip from 'adm-zip' +import { IManifestFile } from './humanInTheLoopManager' export async function getDependenciesFolderInfo(): Promise { const dependencyFolderName = `${CodeWhispererConstants.dependencyFolderName}${globals.clock.Date.now()}` diff --git a/packages/core/src/shared/constants.ts b/packages/core/src/shared/constants.ts index 48e64342e57..95d2aaac309 100644 --- a/packages/core/src/shared/constants.ts +++ b/packages/core/src/shared/constants.ts @@ -4,6 +4,7 @@ */ import * as vscode from 'vscode' +import { manageAccessGuideURL } from '../amazonq/webview/ui/texts/constants' export const profileSettingKey = 'profile' export const productName: string = 'aws-toolkit-vscode' @@ -196,3 +197,11 @@ export const amazonQVscodeMarketplace = export const crashMonitoringDirName = 'crashMonitoring' export const amazonQTabSuffix = '(Generated by Amazon Q)' + +/** + * Common strings used throughout the application + */ + +export const uploadCodeError = `I'm sorry, I couldn't upload your workspace artifacts to Amazon S3 to help you with this task. You might need to allow access to the S3 bucket. For more information, see the [Amazon Q documentation](${manageAccessGuideURL}) or contact your network or organization administrator.` + +export const featureName = 'Amazon Q Developer Agent for software development' diff --git a/packages/core/src/shared/errors.ts b/packages/core/src/shared/errors.ts index 7cec122acaf..5d114043be3 100644 --- a/packages/core/src/shared/errors.ts +++ b/packages/core/src/shared/errors.ts @@ -15,7 +15,7 @@ import type * as os from 'os' import { CodeWhispererStreamingServiceException } from '@amzn/codewhisperer-streaming' import { driveLetterRegex } from './utilities/pathUtils' import { getLogger } from './logger/logger' -import { crashMonitoringDirName } from './constants' +import { crashMonitoringDirName, uploadCodeError } from './constants' import { RequestCancelledError } from './request' let _username = 'unknown-user' @@ -849,6 +849,21 @@ export class ServiceError extends ToolkitError { } } +export class UploadURLExpired extends ClientError { + constructor() { + super( + "I’m sorry, I wasn't able to generate code. A connection timed out or became unavailable. Please try again or check the following:\n\n- Exclude non-essential files in your workspace’s .gitignore.\n\n- Check that your network connection is stable.", + { code: 'UploadURLExpired' } + ) + } +} + +export class UploadCodeError extends ServiceError { + constructor(statusCode: string) { + super(uploadCodeError, { code: `UploadCode-${statusCode}` }) + } +} + export class ContentLengthError extends ClientError { constructor(message: string, info: ErrorInformation = { code: 'ContentLengthError' }) { super(message, info) diff --git a/packages/core/src/shared/utilities/workspaceUtils.ts b/packages/core/src/shared/utilities/workspaceUtils.ts index 12cce75b3ff..122f2a185f4 100644 --- a/packages/core/src/shared/utilities/workspaceUtils.ts +++ b/packages/core/src/shared/utilities/workspaceUtils.ts @@ -19,7 +19,6 @@ import * as parser from '@gerhobbelt/gitignore-parser' import fs from '../fs/fs' import { ChildProcess } from './processUtils' import { isWin } from '../vscode/env' -import { maxRepoSizeBytes } from '../../amazonqFeatureDev/constants' type GitIgnoreRelativeAcceptor = { folderPath: string @@ -378,6 +377,8 @@ export async function collectFiles( const includeContent = options?.includeContent ?? true const maxFileSizeBytes = options?.maxFileSizeBytes ?? 1024 * 1024 * 10 + // Max allowed size for file collection + const maxRepoSizeBytes = 200 * 1024 * 1024 const excludeByGitIgnore = options?.excludeByGitIgnore ?? true const failOnLimit = options?.failOnLimit ?? true const inputExcludePatterns = options?.excludePatterns ?? defaultExcludePatterns diff --git a/packages/core/src/test/amazonq/common/diff.test.ts b/packages/core/src/test/amazonq/common/diff.test.ts index 0fc81403a59..a8f3bea8747 100644 --- a/packages/core/src/test/amazonq/common/diff.test.ts +++ b/packages/core/src/test/amazonq/common/diff.test.ts @@ -13,7 +13,6 @@ import * as path from 'path' import * as vscode from 'vscode' import sinon from 'sinon' import { FileSystem } from '../../../shared/fs/fs' -import { featureDevScheme } from '../../../amazonqFeatureDev' import { createAmazonQUri, getFileDiffUris, @@ -28,6 +27,7 @@ describe('diff', () => { const filePath = path.join('/', 'foo', 'fi') const rightPath = path.join('foo', 'fee') const tabId = '0' + const featureDevScheme = 'aws-featureDev' let sandbox: sinon.SinonSandbox let executeCommandSpy: sinon.SinonSpy diff --git a/packages/core/src/test/amazonq/session/sessionState.test.ts b/packages/core/src/test/amazonq/session/sessionState.test.ts deleted file mode 100644 index dcff3398cea..00000000000 --- a/packages/core/src/test/amazonq/session/sessionState.test.ts +++ /dev/null @@ -1,153 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import sinon from 'sinon' -import { CodeGenBase } from '../../../amazonq/session/sessionState' -import { RunCommandLogFileName } from '../../../amazonq/session/sessionState' -import assert from 'assert' -import * as workspaceUtils from '../../../shared/utilities/workspaceUtils' -import { TelemetryHelper } from '../../../amazonq/util/telemetryHelper' -import { assertLogsContain } from '../../globalSetup.test' - -describe('CodeGenBase generateCode log file handling', () => { - class TestCodeGen extends CodeGenBase { - public generatedFiles: any[] = [] - constructor(config: any, tabID: string) { - super(config, tabID) - } - protected handleProgress(_messenger: any): void { - // No-op for test. - } - protected getScheme(): string { - return 'file' - } - protected getTimeoutErrorCode(): string { - return 'test_timeout' - } - protected handleGenerationComplete(_messenger: any, newFileInfo: any[]): void { - this.generatedFiles = newFileInfo - } - protected handleError(_messenger: any, _codegenResult: any): Error { - throw new Error('handleError called') - } - } - - let fakeProxyClient: any - let testConfig: any - let fsMock: any - let messengerMock: any - let testAction: any - - beforeEach(async () => { - const ret = { - testworkspacefolder: { - uri: vscode.Uri.file('/path/to/testworkspacefolder'), - name: 'testworkspacefolder', - index: 0, - }, - } - sinon.stub(workspaceUtils, 'getWorkspaceFoldersByPrefixes').returns(ret) - - fakeProxyClient = { - getCodeGeneration: sinon.stub().resolves({ - codeGenerationStatus: { status: 'Complete' }, - codeGenerationRemainingIterationCount: 0, - codeGenerationTotalIterationCount: 1, - }), - exportResultArchive: sinon.stub(), - } - - testConfig = { - conversationId: 'conv_test', - uploadId: 'upload_test', - workspaceRoots: ['/path/to/testworkspacefolder'], - proxyClient: fakeProxyClient, - } - - fsMock = { - writeFile: sinon.stub().resolves(), - registerProvider: sinon.stub().resolves(), - } - - messengerMock = { sendAnswer: sinon.spy() } - - testAction = { - fs: fsMock, - messenger: messengerMock, - tokenSource: { - token: { - isCancellationRequested: false, - onCancellationRequested: () => {}, - }, - }, - } - }) - - afterEach(() => { - sinon.restore() - }) - - const runGenerateCode = async (codeGenerationId: string) => { - const testCodeGen = new TestCodeGen(testConfig, 'tab1') - return await testCodeGen.generateCode({ - messenger: messengerMock, - fs: fsMock, - codeGenerationId, - telemetry: new TelemetryHelper(), - workspaceFolders: [testConfig.workspaceRoots[0]], - action: testAction, - }) - } - - const createExpectedNewFile = (fileObj: { zipFilePath: string; fileContent: string }) => ({ - zipFilePath: fileObj.zipFilePath, - fileContent: fileObj.fileContent, - changeApplied: false, - rejected: false, - relativePath: fileObj.zipFilePath, - virtualMemoryUri: vscode.Uri.file(`/upload_test/${fileObj.zipFilePath}`), - workspaceFolder: { - index: 0, - name: 'testworkspacefolder', - uri: vscode.Uri.file('/path/to/testworkspacefolder'), - }, - }) - - it('adds the log content to logger if present and excludes it from new files', async () => { - const logFileInfo = { - zipFilePath: RunCommandLogFileName, - fileContent: 'Log content', - } - const otherFile = { zipFilePath: 'other.ts', fileContent: 'other content' } - fakeProxyClient.exportResultArchive.resolves({ - newFileContents: [logFileInfo, otherFile], - deletedFiles: [], - references: [], - }) - const result = await runGenerateCode('codegen1') - - assertLogsContain(`sessionState: Run Command logs, Log content`, false, 'info') - - const expectedNewFile = createExpectedNewFile(otherFile) - assert.deepStrictEqual(result.newFiles[0].fileContent, expectedNewFile.fileContent) - }) - - it('skips log file handling if log file is not present', async () => { - const file1 = { zipFilePath: 'file1.ts', fileContent: 'content1' } - fakeProxyClient.exportResultArchive.resolves({ - newFileContents: [file1], - deletedFiles: [], - references: [], - }) - - const result = await runGenerateCode('codegen2') - - assert.throws(() => assertLogsContain(`sessionState: Run Command logs, Log content`, false, 'info')) - - const expectedNewFile = createExpectedNewFile(file1) - assert.deepStrictEqual(result.newFiles[0].fileContent, expectedNewFile.fileContent) - }) -}) diff --git a/packages/core/src/test/amazonq/session/testSetup.ts b/packages/core/src/test/amazonq/session/testSetup.ts deleted file mode 100644 index 76f2c90f94f..00000000000 --- a/packages/core/src/test/amazonq/session/testSetup.ts +++ /dev/null @@ -1,74 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import sinon from 'sinon' -import { createBasicTestConfig, createMockSessionStateConfig, TestSessionMocks } from '../utils' -import { SessionStateConfig } from '../../../amazonq' - -export function createSessionTestSetup() { - const conversationId = 'conversation-id' - const uploadId = 'upload-id' - const tabId = 'tab-id' - const currentCodeGenerationId = '' - - return { - conversationId, - uploadId, - tabId, - currentCodeGenerationId, - } -} - -export async function createTestConfig( - testMocks: TestSessionMocks, - conversationId: string, - uploadId: string, - currentCodeGenerationId: string -) { - testMocks.getCodeGeneration = sinon.stub() - testMocks.exportResultArchive = sinon.stub() - testMocks.createUploadUrl = sinon.stub() - const basicConfig = await createBasicTestConfig(conversationId, uploadId, currentCodeGenerationId) - const testConfig = createMockSessionStateConfig(basicConfig, testMocks) - return testConfig -} - -export interface TestContext { - conversationId: string - uploadId: string - tabId: string - currentCodeGenerationId: string - testConfig: SessionStateConfig - testMocks: Record -} - -export function createTestContext(): TestContext { - const { conversationId, uploadId, tabId, currentCodeGenerationId } = createSessionTestSetup() - - return { - conversationId, - uploadId, - tabId, - currentCodeGenerationId, - testConfig: {} as SessionStateConfig, - testMocks: {}, - } -} - -export function setupTestHooks(context: TestContext) { - beforeEach(async () => { - context.testMocks = {} - context.testConfig = await createTestConfig( - context.testMocks, - context.conversationId, - context.uploadId, - context.currentCodeGenerationId - ) - }) - - afterEach(() => { - sinon.restore() - }) -} diff --git a/packages/core/src/test/amazonq/utils.ts b/packages/core/src/test/amazonq/utils.ts deleted file mode 100644 index ec2e7020e4e..00000000000 --- a/packages/core/src/test/amazonq/utils.ts +++ /dev/null @@ -1,182 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import * as sinon from 'sinon' -import { MessagePublisher } from '../../amazonq/messages/messagePublisher' -import { ChatControllerEventEmitters, FeatureDevController } from '../../amazonqFeatureDev/controllers/chat/controller' -import { FeatureDevChatSessionStorage } from '../../amazonqFeatureDev/storages/chatSession' -import { createTestWorkspaceFolder } from '../testUtil' -import { Session } from '../../amazonqFeatureDev/session/session' -import { SessionState, SessionStateAction, SessionStateConfig } from '../../amazonq/commons/types' -import { FeatureDevClient } from '../../amazonqFeatureDev/client/featureDev' -import { VirtualMemoryFile } from '../../shared/virtualMemoryFile' -import path from 'path' -import { featureDevChat } from '../../amazonqFeatureDev/constants' -import { Messenger } from '../../amazonq/commons/connector/baseMessenger' -import { AppToWebViewMessageDispatcher } from '../../amazonq/commons/connector/connectorMessages' -import { createSessionConfig } from '../../amazonq/commons/session/sessionConfigFactory' -import { VirtualFileSystem } from '../../shared' -import { TelemetryHelper } from '../../amazonq/util/telemetryHelper' -import { FeatureClient } from '../../amazonq/client/client' - -export function createMessenger(): Messenger { - return new Messenger( - new AppToWebViewMessageDispatcher(new MessagePublisher(sinon.createStubInstance(vscode.EventEmitter))), - featureDevChat - ) -} - -export function createMockChatEmitters(): ChatControllerEventEmitters { - return { - processHumanChatMessage: new vscode.EventEmitter(), - followUpClicked: new vscode.EventEmitter(), - openDiff: new vscode.EventEmitter(), - processChatItemVotedMessage: new vscode.EventEmitter(), - processChatItemFeedbackMessage: new vscode.EventEmitter(), - stopResponse: new vscode.EventEmitter(), - tabOpened: new vscode.EventEmitter(), - tabClosed: new vscode.EventEmitter(), - authClicked: new vscode.EventEmitter(), - processResponseBodyLinkClick: new vscode.EventEmitter(), - insertCodeAtPositionClicked: new vscode.EventEmitter(), - fileClicked: new vscode.EventEmitter(), - storeCodeResultMessageId: new vscode.EventEmitter(), - } -} - -export interface ControllerSetup { - emitters: ChatControllerEventEmitters - workspaceFolder: vscode.WorkspaceFolder - messenger: Messenger - sessionStorage: FeatureDevChatSessionStorage -} - -export async function createSession({ - messenger, - sessionState, - scheme, - conversationID = '0', - tabID = '0', - uploadID = '0', -}: { - messenger: Messenger - scheme: string - sessionState?: Omit - conversationID?: string - tabID?: string - uploadID?: string -}) { - const sessionConfig = await createSessionConfig(scheme) - - const client = sinon.createStubInstance(FeatureDevClient) - client.createConversation.resolves(conversationID) - const session = new Session(sessionConfig, messenger, tabID, sessionState, client) - - sinon.stub(session, 'conversationId').get(() => conversationID) - sinon.stub(session, 'uploadId').get(() => uploadID) - - return session -} - -export async function sessionRegisterProvider(session: Session, uri: vscode.Uri, fileContents: Uint8Array) { - session.config.fs.registerProvider(uri, new VirtualMemoryFile(fileContents)) -} - -export function generateVirtualMemoryUri(uploadID: string, filePath: string, scheme: string) { - const generationFilePath = path.join(uploadID, filePath) - const uri = vscode.Uri.from({ scheme, path: generationFilePath }) - return uri -} - -export async function sessionWriteFile(session: Session, uri: vscode.Uri, encodedContent: Uint8Array) { - await session.config.fs.writeFile(uri, encodedContent, { - create: true, - overwrite: true, - }) -} - -export async function createController(): Promise { - const messenger = createMessenger() - - // Create a new workspace root - const testWorkspaceFolder = await createTestWorkspaceFolder() - sinon.stub(vscode.workspace, 'workspaceFolders').value([testWorkspaceFolder]) - - const sessionStorage = new FeatureDevChatSessionStorage(messenger) - - const mockChatControllerEventEmitters = createMockChatEmitters() - - new FeatureDevController( - mockChatControllerEventEmitters, - messenger, - sessionStorage, - sinon.createStubInstance(vscode.EventEmitter).event - ) - - return { - emitters: mockChatControllerEventEmitters, - workspaceFolder: testWorkspaceFolder, - messenger, - sessionStorage, - } -} - -export function createMockSessionStateAction(msg?: string): SessionStateAction { - return { - task: 'test-task', - msg: msg ?? 'test-msg', - fs: new VirtualFileSystem(), - messenger: new Messenger( - new AppToWebViewMessageDispatcher(new MessagePublisher(new vscode.EventEmitter())), - featureDevChat - ), - telemetry: new TelemetryHelper(), - uploadHistory: {}, - } -} - -export interface TestSessionMocks { - getCodeGeneration?: sinon.SinonStub - exportResultArchive?: sinon.SinonStub - createUploadUrl?: sinon.SinonStub -} - -export interface SessionTestConfig { - conversationId: string - uploadId: string - workspaceFolder: vscode.WorkspaceFolder - currentCodeGenerationId?: string -} - -export function createMockSessionStateConfig(config: SessionTestConfig, mocks: TestSessionMocks): SessionStateConfig { - return { - workspaceRoots: ['fake-source'], - workspaceFolders: [config.workspaceFolder], - conversationId: config.conversationId, - proxyClient: { - createConversation: () => sinon.stub(), - createUploadUrl: () => mocks.createUploadUrl!(), - startCodeGeneration: () => sinon.stub(), - getCodeGeneration: () => mocks.getCodeGeneration!(), - exportResultArchive: () => mocks.exportResultArchive!(), - } as unknown as FeatureClient, - uploadId: config.uploadId, - currentCodeGenerationId: config.currentCodeGenerationId, - } -} - -export async function createBasicTestConfig( - conversationId: string = 'conversation-id', - uploadId: string = 'upload-id', - currentCodeGenerationId: string = '' -): Promise { - return { - conversationId, - uploadId, - workspaceFolder: await createTestWorkspaceFolder('fake-root'), - currentCodeGenerationId, - } -} diff --git a/packages/core/src/test/amazonqDoc/controller.test.ts b/packages/core/src/test/amazonqDoc/controller.test.ts deleted file mode 100644 index d69edc47fd7..00000000000 --- a/packages/core/src/test/amazonqDoc/controller.test.ts +++ /dev/null @@ -1,577 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -import * as vscode from 'vscode' -import sinon from 'sinon' -import { - assertTelemetry, - ControllerSetup, - createController, - createExpectedEvent, - createExpectedMetricData, - createSession, - EventMetrics, - FollowUpSequences, - generateVirtualMemoryUri, - updateFilePaths, -} from './utils' -import { CurrentWsFolders, MetricDataOperationName, MetricDataResult, NewFileInfo } from '../../amazonqDoc/types' -import { DocCodeGenState, docScheme, Session } from '../../amazonqDoc' -import { AuthUtil } from '../../codewhisperer' -import { - ApiClientError, - ApiServiceError, - CodeIterationLimitError, - FeatureDevClient, - getMetricResult, - MonthlyConversationLimitError, - PrepareRepoFailedError, - TabIdNotFoundError, - UploadCodeError, - UploadURLExpired, - UserMessageNotFoundError, - ZipFileError, -} from '../../amazonqFeatureDev' -import { i18n, ToolkitError, waitUntil } from '../../shared' -import { FollowUpTypes } from '../../amazonq/commons/types' -import { FileSystem } from '../../shared/fs/fs' -import { ReadmeBuilder } from './mockContent' -import * as path from 'path' -import { - ContentLengthError, - NoChangeRequiredException, - PromptRefusalException, - PromptTooVagueError, - PromptUnrelatedError, - ReadmeTooLargeError, - ReadmeUpdateTooLargeError, - WorkspaceEmptyError, -} from '../../amazonqDoc/errors' -import { LlmError } from '../../amazonq/errors' -describe('Controller - Doc Generation', () => { - const firstTabID = '123' - const firstConversationID = '123' - const firstUploadID = '123' - - const secondTabID = '456' - const secondConversationID = '456' - const secondUploadID = '456' - - let controllerSetup: ControllerSetup - let session: Session - let sendDocTelemetrySpy: sinon.SinonStub - let sendDocTelemetrySpyForSecondTab: sinon.SinonStub - let mockGetCodeGeneration: sinon.SinonStub - let getSessionStub: sinon.SinonStub - let modifiedReadme: string - const generatedReadme = ReadmeBuilder.createBaseReadme() - let sandbox: sinon.SinonSandbox - - const getFilePaths = (controllerSetup: ControllerSetup, uploadID: string): NewFileInfo[] => [ - { - zipFilePath: path.normalize('README.md'), - relativePath: path.normalize('README.md'), - fileContent: generatedReadme, - rejected: false, - virtualMemoryUri: generateVirtualMemoryUri(uploadID, path.normalize('README.md'), docScheme), - workspaceFolder: controllerSetup.workspaceFolder, - changeApplied: false, - }, - ] - - async function createCodeGenState( - sandbox: sinon.SinonSandbox, - tabID: string, - conversationID: string, - uploadID: string - ) { - mockGetCodeGeneration = sandbox.stub().resolves({ codeGenerationStatus: { status: 'Complete' } }) - - const workspaceFolders = [controllerSetup.workspaceFolder] as CurrentWsFolders - const testConfig = { - conversationId: conversationID, - proxyClient: { - createConversation: () => sandbox.stub(), - createUploadUrl: () => sandbox.stub(), - generatePlan: () => sandbox.stub(), - startCodeGeneration: () => sandbox.stub(), - getCodeGeneration: () => mockGetCodeGeneration(), - exportResultArchive: () => sandbox.stub(), - } as unknown as FeatureDevClient, - workspaceRoots: [''], - uploadId: uploadID, - workspaceFolders, - } - - const codeGenState = new DocCodeGenState( - testConfig, - getFilePaths(controllerSetup, uploadID), - [], - [], - tabID, - 0, - {} - ) - return createSession({ - messenger: controllerSetup.messenger, - sessionState: codeGenState, - conversationID, - tabID, - uploadID, - scheme: docScheme, - sandbox, - }) - } - async function fireFollowUps(followUpTypes: FollowUpTypes[], stub: sinon.SinonStub, tabID: string) { - for (const type of followUpTypes) { - controllerSetup.emitters.followUpClicked.fire({ - tabID, - followUp: { type }, - }) - await waitForStub(stub) - } - } - - async function waitForStub(stub: sinon.SinonStub) { - await waitUntil(() => Promise.resolve(stub.callCount > 0), {}) - } - - async function performAction( - action: 'generate' | 'update' | 'makeChanges' | 'accept' | 'edit', - getSessionStub: sinon.SinonStub, - message?: string, - tabID = firstTabID, - conversationID = firstConversationID - ) { - const sequences = { - generate: FollowUpSequences.generateReadme, - update: FollowUpSequences.updateReadme, - edit: FollowUpSequences.editReadme, - makeChanges: FollowUpSequences.makeChanges, - accept: FollowUpSequences.acceptContent, - } - - await fireFollowUps(sequences[action], getSessionStub, tabID) - - if ((action === 'makeChanges' || action === 'edit') && message) { - controllerSetup.emitters.processHumanChatMessage.fire({ - tabID, - conversationID, - message, - }) - await waitForStub(getSessionStub) - } - } - - async function setupTest(sandbox: sinon.SinonSandbox, isMultiTabs?: boolean, error?: ToolkitError) { - controllerSetup = await createController(sandbox) - session = await createCodeGenState(sandbox, firstTabID, firstConversationID, firstUploadID) - sendDocTelemetrySpy = sandbox.stub(session, 'sendDocTelemetryEvent').resolves() - sandbox.stub(session, 'preloader').resolves() - error ? sandbox.stub(session, 'send').throws(error) : sandbox.stub(session, 'send').resolves() - Object.defineProperty(session, '_conversationId', { - value: firstConversationID, - writable: true, - configurable: true, - }) - - sandbox.stub(AuthUtil.instance, 'getChatAuthState').resolves({ - codewhispererCore: 'connected', - codewhispererChat: 'connected', - amazonQ: 'connected', - }) - sandbox.stub(FileSystem.prototype, 'exists').resolves(false) - if (isMultiTabs) { - const secondSession = await createCodeGenState(sandbox, secondTabID, secondConversationID, secondUploadID) - sendDocTelemetrySpyForSecondTab = sandbox.stub(secondSession, 'sendDocTelemetryEvent').resolves() - sandbox.stub(secondSession, 'preloader').resolves() - sandbox.stub(secondSession, 'send').resolves() - Object.defineProperty(secondSession, '_conversationId', { - value: secondConversationID, - writable: true, - configurable: true, - }) - getSessionStub = sandbox - .stub(controllerSetup.sessionStorage, 'getSession') - .callsFake(async (tabId: string): Promise => { - if (tabId === firstTabID) { - return session - } - if (tabId === secondTabID) { - return secondSession - } - throw new Error(`Unknown tab ID: ${tabId}`) - }) - } else { - getSessionStub = sandbox.stub(controllerSetup.sessionStorage, 'getSession').resolves(session) - } - modifiedReadme = ReadmeBuilder.createReadmeWithRepoStructure() - sandbox - .stub(vscode.workspace, 'openTextDocument') - .callsFake(async (options?: string | vscode.Uri | { language?: string; content?: string }) => { - let documentPath = '' - if (typeof options === 'string') { - documentPath = options - } else if (options && 'path' in options) { - documentPath = options.path - } - - const isTempFile = documentPath === 'empty' - return { - getText: () => (isTempFile ? generatedReadme : modifiedReadme), - } as any - }) - } - - const retryTest = async ( - testMethod: () => Promise, - isMultiTabs?: boolean, - error?: ToolkitError, - maxRetries: number = 3, - delayMs: number = 1000 - ): Promise => { - let lastError: Error | undefined - - for (let attempt = 1; attempt <= maxRetries + 1; attempt++) { - sandbox = sinon.createSandbox() - sandbox.useFakeTimers({ - now: new Date('2025-03-20T12:00:00.000Z'), - toFake: ['Date'], - }) - try { - await setupTest(sandbox, isMultiTabs, error) - await testMethod() - sandbox.restore() - return - } catch (error) { - lastError = error as Error - sandbox.restore() - - if (attempt > maxRetries) { - console.error(`Test failed after ${maxRetries} retries:`, lastError) - throw lastError - } - - console.log(`Test attempt ${attempt} failed, retrying...`) - await new Promise((resolve) => setTimeout(resolve, delayMs)) - } - } - } - - after(() => { - if (sandbox) { - sandbox.restore() - } - }) - - it('should emit generation telemetry for initial README generation', async () => { - await retryTest(async () => { - await performAction('generate', getSessionStub) - - const expectedEvent = createExpectedEvent({ - type: 'generation', - ...EventMetrics.INITIAL_README, - interactionType: 'GENERATE_README', - conversationId: firstConversationID, - }) - - await assertTelemetry({ - spy: sendDocTelemetrySpy, - expectedEvent, - type: 'generation', - sandbox, - }) - }) - }) - it('should emit another generation telemetry for make changes operation after initial README generation', async () => { - await retryTest(async () => { - await performAction('generate', getSessionStub) - const firstExpectedEvent = createExpectedEvent({ - type: 'generation', - ...EventMetrics.INITIAL_README, - interactionType: 'GENERATE_README', - conversationId: firstConversationID, - }) - - await assertTelemetry({ - spy: sendDocTelemetrySpy, - expectedEvent: firstExpectedEvent, - type: 'generation', - sandbox, - }) - - await updateFilePaths(session, modifiedReadme, firstUploadID, docScheme, controllerSetup.workspaceFolder) - await performAction('makeChanges', getSessionStub, 'add repository structure section') - - const secondExpectedEvent = createExpectedEvent({ - type: 'generation', - ...EventMetrics.REPO_STRUCTURE, - interactionType: 'GENERATE_README', - conversationId: firstConversationID, - }) - - await assertTelemetry({ - spy: sendDocTelemetrySpy, - expectedEvent: secondExpectedEvent, - type: 'generation', - sandbox, - }) - }) - }) - - it('should emit acceptance telemetry for README generation', async () => { - await retryTest(async () => { - await performAction('generate', getSessionStub) - await new Promise((resolve) => setTimeout(resolve, 100)) - const expectedEvent = createExpectedEvent({ - type: 'acceptance', - ...EventMetrics.INITIAL_README, - interactionType: 'GENERATE_README', - conversationId: firstConversationID, - }) - - await performAction('accept', getSessionStub) - await assertTelemetry({ - spy: sendDocTelemetrySpy, - expectedEvent, - type: 'acceptance', - sandbox, - }) - }) - }) - it('should emit generation telemetry for README update', async () => { - await retryTest(async () => { - await performAction('update', getSessionStub) - - const expectedEvent = createExpectedEvent({ - type: 'generation', - ...EventMetrics.REPO_STRUCTURE, - interactionType: 'UPDATE_README', - conversationId: firstConversationID, - }) - - await assertTelemetry({ - spy: sendDocTelemetrySpy, - expectedEvent, - type: 'generation', - sandbox, - }) - }) - }) - it('should emit another generation telemetry for make changes operation after README update', async () => { - await retryTest(async () => { - await performAction('update', getSessionStub) - await new Promise((resolve) => setTimeout(resolve, 100)) - - modifiedReadme = ReadmeBuilder.createReadmeWithDataFlow() - await updateFilePaths(session, modifiedReadme, firstUploadID, docScheme, controllerSetup.workspaceFolder) - - await performAction('makeChanges', getSessionStub, 'add data flow section') - - const expectedEvent = createExpectedEvent({ - type: 'generation', - ...EventMetrics.DATA_FLOW, - interactionType: 'UPDATE_README', - conversationId: firstConversationID, - }) - - await assertTelemetry({ - spy: sendDocTelemetrySpy, - expectedEvent, - type: 'generation', - sandbox, - }) - }) - }) - - it('should emit acceptance telemetry for README update', async () => { - await retryTest(async () => { - await performAction('update', getSessionStub) - await new Promise((resolve) => setTimeout(resolve, 100)) - - const expectedEvent = createExpectedEvent({ - type: 'acceptance', - ...EventMetrics.REPO_STRUCTURE, - interactionType: 'UPDATE_README', - conversationId: firstConversationID, - }) - - await performAction('accept', getSessionStub) - await assertTelemetry({ - spy: sendDocTelemetrySpy, - expectedEvent, - type: 'acceptance', - sandbox, - }) - }) - }) - - it('should emit generation telemetry for README edit', async () => { - await retryTest(async () => { - await performAction('edit', getSessionStub, 'add repository structure section') - - const expectedEvent = createExpectedEvent({ - type: 'generation', - ...EventMetrics.REPO_STRUCTURE, - interactionType: 'EDIT_README', - conversationId: firstConversationID, - }) - - await assertTelemetry({ - spy: sendDocTelemetrySpy, - expectedEvent, - type: 'generation', - sandbox, - }) - }) - }) - it('should emit acceptance telemetry for README edit', async () => { - await retryTest(async () => { - await performAction('edit', getSessionStub, 'add repository structure section') - await new Promise((resolve) => setTimeout(resolve, 100)) - - const expectedEvent = createExpectedEvent({ - type: 'acceptance', - ...EventMetrics.REPO_STRUCTURE, - interactionType: 'EDIT_README', - conversationId: firstConversationID, - }) - - await performAction('accept', getSessionStub) - await assertTelemetry({ - spy: sendDocTelemetrySpy, - expectedEvent, - type: 'acceptance', - sandbox, - }) - }) - }) - it('should emit separate telemetry events when executing /doc in different tabs', async () => { - await retryTest(async () => { - const firstSession = await getSessionStub(firstTabID) - const secondSession = await getSessionStub(secondTabID) - await performAction('generate', firstSession) - await performAction('update', secondSession, undefined, secondTabID, secondConversationID) - - const expectedEvent = createExpectedEvent({ - type: 'generation', - ...EventMetrics.INITIAL_README, - interactionType: 'GENERATE_README', - conversationId: firstConversationID, - }) - - await assertTelemetry({ - spy: sendDocTelemetrySpy, - expectedEvent, - type: 'generation', - sandbox, - }) - - const expectedEventForSecondTab = createExpectedEvent({ - type: 'generation', - ...EventMetrics.REPO_STRUCTURE, - interactionType: 'UPDATE_README', - conversationId: secondConversationID, - }) - - await assertTelemetry({ - spy: sendDocTelemetrySpyForSecondTab, - expectedEvent: expectedEventForSecondTab, - type: 'generation', - sandbox, - }) - }, true) - }) - - describe('Doc Generation Error Handling', () => { - const errors = [ - { - name: 'MonthlyConversationLimitError', - error: new MonthlyConversationLimitError('Service Quota Exceeded'), - }, - { - name: 'DocGenerationGuardrailsException', - error: new ApiClientError( - i18n('AWS.amazonq.doc.error.docGen.default'), - 'GetTaskAssistCodeGeneration', - 'GuardrailsException', - 400 - ), - }, - { - name: 'DocGenerationEmptyPatchException', - error: new LlmError(i18n('AWS.amazonq.doc.error.docGen.default'), { - code: 'EmptyPatchException', - }), - }, - { - name: 'DocGenerationThrottlingException', - error: new ApiClientError( - i18n('AWS.amazonq.featureDev.error.throttling'), - 'GetTaskAssistCodeGeneration', - 'ThrottlingException', - 429 - ), - }, - { name: 'UploadCodeError', error: new UploadCodeError('403: Forbiden') }, - { name: 'UserMessageNotFoundError', error: new UserMessageNotFoundError() }, - { name: 'TabIdNotFoundError', error: new TabIdNotFoundError() }, - { name: 'PrepareRepoFailedError', error: new PrepareRepoFailedError() }, - { name: 'PromptRefusalException', error: new PromptRefusalException(0) }, - { name: 'ZipFileError', error: new ZipFileError() }, - { name: 'CodeIterationLimitError', error: new CodeIterationLimitError() }, - { name: 'UploadURLExpired', error: new UploadURLExpired() }, - { name: 'NoChangeRequiredException', error: new NoChangeRequiredException() }, - { name: 'ReadmeTooLargeError', error: new ReadmeTooLargeError() }, - { name: 'ReadmeUpdateTooLargeError', error: new ReadmeUpdateTooLargeError(0) }, - { name: 'ContentLengthError', error: new ContentLengthError() }, - { name: 'WorkspaceEmptyError', error: new WorkspaceEmptyError() }, - { name: 'PromptUnrelatedError', error: new PromptUnrelatedError(0) }, - { name: 'PromptTooVagueError', error: new PromptTooVagueError(0) }, - { name: 'PromptRefusalException', error: new PromptRefusalException(0) }, - { - name: 'default', - error: new ApiServiceError( - i18n('AWS.amazonq.doc.error.docGen.default'), - 'GetTaskAssistCodeGeneration', - 'UnknownException', - 500 - ), - }, - ] - for (const { name, error } of errors) { - it(`should emit failure operation telemetry when ${name} occurs`, async () => { - await retryTest( - async () => { - await performAction('generate', getSessionStub) - - const expectedSuccessMetric = createExpectedMetricData( - MetricDataOperationName.StartDocGeneration, - MetricDataResult.Success - ) - await assertTelemetry({ - spy: sendDocTelemetrySpy, - expectedEvent: expectedSuccessMetric, - type: 'metric', - sandbox, - }) - - const expectedFailureMetric = createExpectedMetricData( - MetricDataOperationName.EndDocGeneration, - getMetricResult(error) - ) - await assertTelemetry({ - spy: sendDocTelemetrySpy, - expectedEvent: expectedFailureMetric, - type: 'metric', - sandbox, - }) - }, - undefined, - error - ) - }) - } - }) -}) diff --git a/packages/core/src/test/amazonqDoc/mockContent.ts b/packages/core/src/test/amazonqDoc/mockContent.ts deleted file mode 100644 index 1f3e68f6a58..00000000000 --- a/packages/core/src/test/amazonqDoc/mockContent.ts +++ /dev/null @@ -1,86 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -export const ReadmeSections = { - HEADER: `# My Awesome Project - -This is a demo project showcasing various features and capabilities.`, - - GETTING_STARTED: `## Getting Started -1. Clone the repository -2. Run npm install -3. Start the application`, - - FEATURES: `## Features -- Fast processing -- Easy to use -- Well documented`, - - LICENSE: '## License\nMIT License', - - REPO_STRUCTURE: `## Repository Structure -/src - /components - /utils -/tests - /unit -/docs`, - - DATA_FLOW: `## Data Flow -1. Input processing - - Data validation - - Format conversion -2. Core processing - - Business logic - - Data transformation -3. Output generation - - Result formatting - - Response delivery`, -} as const - -export class ReadmeBuilder { - private sections: string[] = [] - - addSection(section: string): this { - this.sections.push(section.replace(/\r\n/g, '\n')) - return this - } - - build(): string { - return this.sections.join('\n\n').replace(/\r\n/g, '\n') - } - - static createBaseReadme(): string { - return new ReadmeBuilder() - .addSection(ReadmeBuilder.normalizeSection(ReadmeSections.HEADER)) - .addSection(ReadmeBuilder.normalizeSection(ReadmeSections.GETTING_STARTED)) - .addSection(ReadmeBuilder.normalizeSection(ReadmeSections.FEATURES)) - .addSection(ReadmeBuilder.normalizeSection(ReadmeSections.LICENSE)) - .build() - } - - static createReadmeWithRepoStructure(): string { - return new ReadmeBuilder() - .addSection(ReadmeBuilder.normalizeSection(ReadmeSections.HEADER)) - .addSection(ReadmeBuilder.normalizeSection(ReadmeSections.REPO_STRUCTURE)) - .addSection(ReadmeBuilder.normalizeSection(ReadmeSections.GETTING_STARTED)) - .addSection(ReadmeBuilder.normalizeSection(ReadmeSections.FEATURES)) - .addSection(ReadmeBuilder.normalizeSection(ReadmeSections.LICENSE)) - .build() - } - - static createReadmeWithDataFlow(): string { - return new ReadmeBuilder() - .addSection(ReadmeBuilder.normalizeSection(ReadmeSections.HEADER)) - .addSection(ReadmeBuilder.normalizeSection(ReadmeSections.GETTING_STARTED)) - .addSection(ReadmeBuilder.normalizeSection(ReadmeSections.FEATURES)) - .addSection(ReadmeBuilder.normalizeSection(ReadmeSections.DATA_FLOW)) - .addSection(ReadmeBuilder.normalizeSection(ReadmeSections.LICENSE)) - .build() - } - - private static normalizeSection(section: string): string { - return section.replace(/\r\n/g, '\n') - } -} diff --git a/packages/core/src/test/amazonqDoc/session/sessionState.test.ts b/packages/core/src/test/amazonqDoc/session/sessionState.test.ts deleted file mode 100644 index 8f96894cc22..00000000000 --- a/packages/core/src/test/amazonqDoc/session/sessionState.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import assert from 'assert' -import sinon from 'sinon' -import { DocPrepareCodeGenState } from '../../../amazonqDoc' -import { createMockSessionStateAction } from '../../amazonq/utils' - -import { createTestContext, setupTestHooks } from '../../amazonq/session/testSetup' - -describe('sessionStateDoc', () => { - const context = createTestContext() - setupTestHooks(context) - - describe('DocPrepareCodeGenState', () => { - it('error when failing to prepare repo information', async () => { - sinon.stub(vscode.workspace, 'findFiles').throws() - context.testMocks.createUploadUrl!.resolves({ uploadId: '', uploadUrl: '' }) - const testAction = createMockSessionStateAction() - - await assert.rejects(() => { - return new DocPrepareCodeGenState(context.testConfig, [], [], [], context.tabId, 0).interact(testAction) - }) - }) - }) -}) diff --git a/packages/core/src/test/amazonqDoc/utils.ts b/packages/core/src/test/amazonqDoc/utils.ts deleted file mode 100644 index 51c7305902c..00000000000 --- a/packages/core/src/test/amazonqDoc/utils.ts +++ /dev/null @@ -1,269 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import * as sinon from 'sinon' -import { MessagePublisher } from '../../amazonq/messages/messagePublisher' -import { ChatControllerEventEmitters, DocController } from '../../amazonqDoc/controllers/chat/controller' -import { DocChatSessionStorage } from '../../amazonqDoc/storages/chatSession' -import { createTestWorkspaceFolder } from '../testUtil' -import { Session } from '../../amazonqDoc/session/session' -import { NewFileInfo, SessionState } from '../../amazonqDoc/types' -import { FeatureDevClient } from '../../amazonqFeatureDev/client/featureDev' -import { VirtualMemoryFile } from '../../shared/virtualMemoryFile' -import path from 'path' -import { docChat } from '../../amazonqDoc/constants' -import { DocMessenger } from '../../amazonqDoc/messenger' -import { AppToWebViewMessageDispatcher } from '../../amazonq/commons/connector/connectorMessages' -import { createSessionConfig } from '../../amazonq/commons/session/sessionConfigFactory' -import { - DocV2GenerationEvent, - DocV2AcceptanceEvent, - MetricData, -} from '../../amazonqFeatureDev/client/featuredevproxyclient' -import { FollowUpTypes } from '../../amazonq/commons/types' - -export function createMessenger(sandbox: sinon.SinonSandbox): DocMessenger { - return new DocMessenger( - new AppToWebViewMessageDispatcher(new MessagePublisher(sandbox.createStubInstance(vscode.EventEmitter))), - docChat - ) -} - -export function createMockChatEmitters(): ChatControllerEventEmitters { - return { - processHumanChatMessage: new vscode.EventEmitter(), - followUpClicked: new vscode.EventEmitter(), - openDiff: new vscode.EventEmitter(), - processChatItemVotedMessage: new vscode.EventEmitter(), - processChatItemFeedbackMessage: new vscode.EventEmitter(), - stopResponse: new vscode.EventEmitter(), - tabOpened: new vscode.EventEmitter(), - tabClosed: new vscode.EventEmitter(), - authClicked: new vscode.EventEmitter(), - processResponseBodyLinkClick: new vscode.EventEmitter(), - insertCodeAtPositionClicked: new vscode.EventEmitter(), - fileClicked: new vscode.EventEmitter(), - formActionClicked: new vscode.EventEmitter(), - } -} - -export interface ControllerSetup { - emitters: ChatControllerEventEmitters - workspaceFolder: vscode.WorkspaceFolder - messenger: DocMessenger - sessionStorage: DocChatSessionStorage -} - -export async function createSession({ - messenger, - sessionState, - scheme, - conversationID = '0', - tabID = '0', - uploadID = '0', - sandbox, -}: { - messenger: DocMessenger - scheme: string - sessionState?: Omit - conversationID?: string - tabID?: string - uploadID?: string - sandbox: sinon.SinonSandbox -}) { - const sessionConfig = await createSessionConfig(scheme) - - const client = sandbox.createStubInstance(FeatureDevClient) - client.createConversation.resolves(conversationID) - const session = new Session(sessionConfig, messenger, tabID, sessionState, client) - - sandbox.stub(session, 'conversationId').get(() => conversationID) - sandbox.stub(session, 'uploadId').get(() => uploadID) - - return session -} -export async function sessionRegisterProvider(session: Session, uri: vscode.Uri, fileContents: Uint8Array) { - session.config.fs.registerProvider(uri, new VirtualMemoryFile(fileContents)) -} - -export function generateVirtualMemoryUri(uploadID: string, filePath: string, scheme: string) { - const generationFilePath = path.join(uploadID, filePath) - const uri = vscode.Uri.from({ scheme, path: generationFilePath }) - return uri -} - -export async function sessionWriteFile(session: Session, uri: vscode.Uri, encodedContent: Uint8Array) { - await session.config.fs.writeFile(uri, encodedContent, { - create: true, - overwrite: true, - }) -} - -export async function createController(sandbox: sinon.SinonSandbox): Promise { - const messenger = createMessenger(sandbox) - - // Create a new workspace root - const testWorkspaceFolder = await createTestWorkspaceFolder() - sandbox.stub(vscode.workspace, 'workspaceFolders').value([testWorkspaceFolder]) - - const sessionStorage = new DocChatSessionStorage(messenger) - - const mockChatControllerEventEmitters = createMockChatEmitters() - - new DocController( - mockChatControllerEventEmitters, - messenger, - sessionStorage, - sandbox.createStubInstance(vscode.EventEmitter).event - ) - - return { - emitters: mockChatControllerEventEmitters, - workspaceFolder: testWorkspaceFolder, - messenger, - sessionStorage, - } -} - -export type EventParams = { - type: 'generation' | 'acceptance' - chars: number - lines: number - files: number - interactionType: 'GENERATE_README' | 'UPDATE_README' | 'EDIT_README' - callIndex?: number - conversationId: string -} -/** - * Metrics for measuring README content changes in documentation generation tests. - */ -export const EventMetrics = { - /** - * Initial README content measurements - * Generated using ReadmeBuilder.createBaseReadme() - */ - INITIAL_README: { - chars: 265, - lines: 16, - files: 1, - }, - /** - * Repository Structure section measurements - * Differential metrics when adding repository structure documentation compare to the initial readme - */ - REPO_STRUCTURE: { - chars: 60, - lines: 8, - files: 1, - }, - /** - * Data Flow section measurements - * Differential metrics when adding data flow documentation compare to the initial readme - */ - DATA_FLOW: { - chars: 180, - lines: 11, - files: 1, - }, -} as const - -export function createExpectedEvent(params: EventParams) { - const baseEvent = { - conversationId: params.conversationId, - numberOfNavigations: 1, - folderLevel: 'ENTIRE_WORKSPACE', - interactionType: params.interactionType, - } - - if (params.type === 'generation') { - return { - ...baseEvent, - numberOfGeneratedChars: params.chars, - numberOfGeneratedLines: params.lines, - numberOfGeneratedFiles: params.files, - } as DocV2GenerationEvent - } else { - return { - ...baseEvent, - numberOfAddedChars: params.chars, - numberOfAddedLines: params.lines, - numberOfAddedFiles: params.files, - userDecision: 'ACCEPT', - } as DocV2AcceptanceEvent - } -} - -export function createExpectedMetricData(operationName: string, result: string) { - return { - metricName: 'Operation', - metricValue: 1, - timestamp: new Date(), - product: 'DocGeneration', - dimensions: [ - { - name: 'operationName', - value: operationName, - }, - { - name: 'result', - value: result, - }, - ], - } -} - -export async function assertTelemetry(params: { - spy: sinon.SinonStub - expectedEvent: DocV2GenerationEvent | DocV2AcceptanceEvent | MetricData - type: 'generation' | 'acceptance' | 'metric' - sandbox: sinon.SinonSandbox -}) { - await new Promise((resolve) => setTimeout(resolve, 100)) - params.sandbox.assert.calledWith(params.spy, params.sandbox.match(params.expectedEvent), params.type) -} - -export async function updateFilePaths( - session: Session, - content: string, - uploadId: string, - docScheme: string, - workspaceFolder: any -) { - const updatedFilePaths: NewFileInfo[] = [ - { - zipFilePath: path.normalize('README.md'), - relativePath: path.normalize('README.md'), - fileContent: content, - rejected: false, - virtualMemoryUri: generateVirtualMemoryUri(uploadId, path.normalize('README.md'), docScheme), - workspaceFolder: workspaceFolder, - changeApplied: false, - }, - ] - - Object.defineProperty(session.state, 'filePaths', { - get: () => updatedFilePaths, - configurable: true, - }) -} - -export const FollowUpSequences = { - generateReadme: [FollowUpTypes.NewTask, FollowUpTypes.CreateDocumentation, FollowUpTypes.ProceedFolderSelection], - updateReadme: [ - FollowUpTypes.NewTask, - FollowUpTypes.UpdateDocumentation, - FollowUpTypes.SynchronizeDocumentation, - FollowUpTypes.ProceedFolderSelection, - ], - editReadme: [ - FollowUpTypes.NewTask, - FollowUpTypes.UpdateDocumentation, - FollowUpTypes.EditDocumentation, - FollowUpTypes.ProceedFolderSelection, - ], - makeChanges: [FollowUpTypes.MakeChanges], - acceptContent: [FollowUpTypes.AcceptChanges], -} diff --git a/packages/core/src/test/amazonqFeatureDev/controllers/chat/controller.test.ts b/packages/core/src/test/amazonqFeatureDev/controllers/chat/controller.test.ts deleted file mode 100644 index 7848d0561b0..00000000000 --- a/packages/core/src/test/amazonqFeatureDev/controllers/chat/controller.test.ts +++ /dev/null @@ -1,717 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import * as assert from 'assert' -import * as path from 'path' -import sinon from 'sinon' -import { waitUntil } from '../../../../shared/utilities/timeoutUtils' -import { ControllerSetup, createController, createSession, generateVirtualMemoryUri } from '../../../amazonq/utils' -import { - CurrentWsFolders, - DeletedFileInfo, - MetricDataOperationName, - MetricDataResult, - NewFileInfo, -} from '../../../../amazonq/commons/types' -import { Session } from '../../../../amazonqFeatureDev/session/session' -import { Prompter } from '../../../../shared/ui/prompter' -import { assertTelemetry, toFile } from '../../../testUtil' -import { - CodeIterationLimitError, - ContentLengthError, - createUserFacingErrorMessage, - FeatureDevServiceError, - getMetricResult, - MonthlyConversationLimitError, - NoChangeRequiredException, - PrepareRepoFailedError, - PromptRefusalException, - SelectedFolderNotInWorkspaceFolderError, - TabIdNotFoundError, - UploadCodeError, - UploadURLExpired, - UserMessageNotFoundError, - ZipFileError, -} from '../../../../amazonqFeatureDev/errors' -import { - FeatureDevCodeGenState, - FeatureDevPrepareCodeGenState, -} from '../../../../amazonqFeatureDev/session/sessionState' -import { FeatureDevClient } from '../../../../amazonqFeatureDev/client/featureDev' -import { createAmazonQUri } from '../../../../amazonq/commons/diff' -import { AuthUtil } from '../../../../codewhisperer' -import { featureDevScheme, featureName, messageWithConversationId } from '../../../../amazonqFeatureDev' -import { i18n } from '../../../../shared/i18n-helper' -import { FollowUpTypes } from '../../../../amazonq/commons/types' -import { ToolkitError } from '../../../../shared' -import { MessengerTypes } from '../../../../amazonqFeatureDev/controllers/chat/messenger/constants' - -let mockGetCodeGeneration: sinon.SinonStub -describe('Controller', () => { - const tabID = '123' - const conversationID = '456' - const uploadID = '789' - - let session: Session - let controllerSetup: ControllerSetup - - const getFilePaths = (controllerSetup: ControllerSetup): NewFileInfo[] => [ - { - zipFilePath: 'myfile1.js', - relativePath: 'myfile1.js', - fileContent: '', - rejected: false, - virtualMemoryUri: generateVirtualMemoryUri(uploadID, 'myfile1.js', featureDevScheme), - workspaceFolder: controllerSetup.workspaceFolder, - changeApplied: false, - }, - { - zipFilePath: 'myfile2.js', - relativePath: 'myfile2.js', - fileContent: '', - rejected: true, - virtualMemoryUri: generateVirtualMemoryUri(uploadID, 'myfile2.js', featureDevScheme), - workspaceFolder: controllerSetup.workspaceFolder, - changeApplied: false, - }, - ] - - const getDeletedFiles = (): DeletedFileInfo[] => [ - { - zipFilePath: 'myfile3.js', - relativePath: 'myfile3.js', - rejected: false, - workspaceFolder: controllerSetup.workspaceFolder, - changeApplied: false, - }, - { - zipFilePath: 'myfile4.js', - relativePath: 'myfile4.js', - rejected: true, - workspaceFolder: controllerSetup.workspaceFolder, - changeApplied: false, - }, - ] - - async function createCodeGenState() { - mockGetCodeGeneration = sinon.stub().resolves({ codeGenerationStatus: { status: 'Complete' } }) - - const workspaceFolders = [controllerSetup.workspaceFolder] as CurrentWsFolders - const testConfig = { - conversationId: conversationID, - proxyClient: { - createConversation: () => sinon.stub(), - createUploadUrl: () => sinon.stub(), - generatePlan: () => sinon.stub(), - startCodeGeneration: () => sinon.stub(), - getCodeGeneration: () => mockGetCodeGeneration(), - exportResultArchive: () => sinon.stub(), - } as unknown as FeatureDevClient, - workspaceRoots: [''], - uploadId: uploadID, - workspaceFolders, - } - - const codeGenState = new FeatureDevCodeGenState(testConfig, getFilePaths(controllerSetup), [], [], tabID, 0, {}) - const newSession = await createSession({ - messenger: controllerSetup.messenger, - sessionState: codeGenState, - conversationID, - tabID, - uploadID, - scheme: featureDevScheme, - }) - return newSession - } - - before(() => { - sinon.stub(performance, 'now').returns(0) - }) - - beforeEach(async () => { - controllerSetup = await createController() - session = await createSession({ - messenger: controllerSetup.messenger, - conversationID, - tabID, - uploadID, - scheme: featureDevScheme, - }) - - sinon.stub(AuthUtil.instance, 'getChatAuthState').resolves({ - codewhispererCore: 'connected', - codewhispererChat: 'connected', - amazonQ: 'connected', - }) - }) - - afterEach(() => { - sinon.restore() - }) - - describe('openDiff', async () => { - async function openDiff(filePath: string, deleted = false) { - const executeDiff = sinon.stub(vscode.commands, 'executeCommand').returns(Promise.resolve(undefined)) - controllerSetup.emitters.openDiff.fire({ tabID, conversationID, filePath, deleted }) - - // Wait until the controller has time to process the event - await waitUntil(() => { - return Promise.resolve(executeDiff.callCount > 0) - }, {}) - - return executeDiff - } - - it('uses empty file when file is not found locally', async () => { - sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session) - const executedDiff = await openDiff(path.join('src', 'mynewfile.js')) - assert.strictEqual( - executedDiff.calledWith( - 'vscode.diff', - createAmazonQUri('empty', tabID, featureDevScheme), - createAmazonQUri(path.join(uploadID, 'src', 'mynewfile.js'), tabID, featureDevScheme) - ), - true - ) - - assertTelemetry('amazonq_isReviewedChanges', { amazonqConversationId: conversationID, enabled: true }) - }) - - it('uses file location when file is found locally and /src is not available', async () => { - sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session) - const newFileLocation = path.join(controllerSetup.workspaceFolder.uri.fsPath, 'mynewfile.js') - await toFile('', newFileLocation) - const executedDiff = await openDiff('mynewfile.js') - assert.strictEqual( - executedDiff.calledWith( - 'vscode.diff', - vscode.Uri.file(newFileLocation), - createAmazonQUri(path.join(uploadID, 'mynewfile.js'), tabID, featureDevScheme) - ), - true - ) - - assertTelemetry('amazonq_isReviewedChanges', { amazonqConversationId: conversationID, enabled: true }) - }) - - it('uses file location when file is found locally and /src is available', async () => { - sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session) - const newFileLocation = path.join(controllerSetup.workspaceFolder.uri.fsPath, 'src', 'mynewfile.js') - await toFile('', newFileLocation) - const executedDiff = await openDiff(path.join('src', 'mynewfile.js')) - assert.strictEqual( - executedDiff.calledWith( - 'vscode.diff', - vscode.Uri.file(newFileLocation), - createAmazonQUri(path.join(uploadID, 'src', 'mynewfile.js'), tabID, featureDevScheme) - ), - true - ) - - assertTelemetry('amazonq_isReviewedChanges', { amazonqConversationId: conversationID, enabled: true }) - }) - - it('uses file location when file is found locally and source folder was picked', async () => { - sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session) - const newFileLocation = path.join(controllerSetup.workspaceFolder.uri.fsPath, 'foo', 'fi', 'mynewfile.js') - await toFile('', newFileLocation) - sinon.stub(vscode.workspace, 'getWorkspaceFolder').returns(controllerSetup.workspaceFolder) - session.config.workspaceRoots = [path.join(controllerSetup.workspaceFolder.uri.fsPath, 'foo', 'fi')] - const executedDiff = await openDiff(path.join('foo', 'fi', 'mynewfile.js')) - assert.strictEqual( - executedDiff.calledWith( - 'vscode.diff', - vscode.Uri.file(newFileLocation), - createAmazonQUri(path.join(uploadID, 'foo', 'fi', 'mynewfile.js'), tabID, featureDevScheme) - ), - true - ) - - assertTelemetry('amazonq_isReviewedChanges', { amazonqConversationId: conversationID, enabled: true }) - }) - }) - - describe('modifyDefaultSourceFolder', () => { - async function modifyDefaultSourceFolder(sourceRoot: string) { - const promptStub = sinon.stub(Prompter.prototype, 'prompt').resolves(vscode.Uri.file(sourceRoot)) - controllerSetup.emitters.followUpClicked.fire({ - tabID, - followUp: { - type: FollowUpTypes.ModifyDefaultSourceFolder, - }, - }) - - // Wait until the controller has time to process the event - await waitUntil(() => { - return Promise.resolve(promptStub.callCount > 0) - }, {}) - - return controllerSetup.sessionStorage.getSession(tabID) - } - - it('fails if selected folder is not under a workspace folder', async () => { - sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session) - sinon.stub(vscode.workspace, 'getWorkspaceFolder').returns(undefined) - const messengerSpy = sinon.spy(controllerSetup.messenger, 'sendAnswer') - await modifyDefaultSourceFolder('../../') - assert.deepStrictEqual( - messengerSpy.calledWith({ - tabID, - type: 'answer', - message: new SelectedFolderNotInWorkspaceFolderError().message, - canBeVoted: true, - }), - true - ) - assert.deepStrictEqual( - messengerSpy.calledWith({ - tabID, - type: 'system-prompt', - followUps: sinon.match.any, - }), - true - ) - }) - - it('accepts valid source folders under a workspace root', async () => { - sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session) - sinon.stub(vscode.workspace, 'getWorkspaceFolder').returns(controllerSetup.workspaceFolder) - const expectedSourceRoot = path.join(controllerSetup.workspaceFolder.uri.fsPath, 'src') - const modifiedSession = await modifyDefaultSourceFolder(expectedSourceRoot) - assert.strictEqual(modifiedSession.config.workspaceRoots.length, 1) - assert.strictEqual(modifiedSession.config.workspaceRoots[0], expectedSourceRoot) - }) - }) - - describe('newTask', () => { - async function newTaskClicked() { - const getSessionStub = sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session) - - controllerSetup.emitters.followUpClicked.fire({ - tabID, - followUp: { - type: FollowUpTypes.NewTask, - }, - }) - - // Wait until the controller has time to process the event - await waitUntil(() => { - return Promise.resolve(getSessionStub.callCount > 0) - }, {}) - } - - it('end chat telemetry is sent', async () => { - await newTaskClicked() - - assertTelemetry('amazonq_endChat', { amazonqConversationId: conversationID, result: 'Succeeded' }) - }) - }) - - describe('fileClicked', () => { - async function fileClicked( - getSessionStub: sinon.SinonStub<[tabID: string], Promise>, - action: string, - filePath: string - ) { - controllerSetup.emitters.fileClicked.fire({ - tabID, - conversationID, - filePath, - action, - }) - - // Wait until the controller has time to process the event - await waitUntil(() => { - return Promise.resolve(getSessionStub.callCount > 0) - }, {}) - return getSessionStub.getCall(0).returnValue - } - - it('clicking the "Reject File" button updates the file state to "rejected: true"', async () => { - const filePath = getFilePaths(controllerSetup)[0].zipFilePath - const session = await createCodeGenState() - const getSessionStub = sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session) - - const rejectFile = await fileClicked(getSessionStub, 'reject-change', filePath) - assert.strictEqual(rejectFile.state.filePaths?.find((i) => i.relativePath === filePath)?.rejected, true) - }) - - it('clicking the "Reject File" button and then "Revert Reject File", updates the file state to "rejected: false"', async () => { - const filePath = getFilePaths(controllerSetup)[0].zipFilePath - const session = await createCodeGenState() - const getSessionStub = sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session) - - await fileClicked(getSessionStub, 'reject-change', filePath) - const revertRejection = await fileClicked(getSessionStub, 'revert-rejection', filePath) - assert.strictEqual( - revertRejection.state.filePaths?.find((i) => i.relativePath === filePath)?.rejected, - false - ) - }) - }) - - describe('insertCode', () => { - it('sets the number of files accepted counting also deleted files', async () => { - async function insertCode() { - const initialState = new FeatureDevPrepareCodeGenState( - { - conversationId: conversationID, - proxyClient: new FeatureDevClient(), - workspaceRoots: [''], - workspaceFolders: [controllerSetup.workspaceFolder], - uploadId: uploadID, - }, - getFilePaths(controllerSetup), - getDeletedFiles(), - [], - tabID, - 0 - ) - - const newSession = await createSession({ - messenger: controllerSetup.messenger, - sessionState: initialState, - conversationID, - tabID, - uploadID, - scheme: featureDevScheme, - }) - const getSessionStub = sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(newSession) - - controllerSetup.emitters.followUpClicked.fire({ - tabID, - conversationID, - followUp: { - type: FollowUpTypes.InsertCode, - }, - }) - - // Wait until the controller has time to process the event - await waitUntil(() => { - return Promise.resolve(getSessionStub.callCount > 0) - }, {}) - } - - await insertCode() - - assertTelemetry('amazonq_isAcceptedCodeChanges', { - amazonqConversationId: conversationID, - amazonqNumberOfFilesAccepted: 2, - enabled: true, - result: 'Succeeded', - }) - }) - }) - - describe('processUserChatMessage', function () { - // TODO: fix disablePreviousFileList error - const runs = [ - { name: 'ContentLengthError', error: new ContentLengthError() }, - { - name: 'MonthlyConversationLimitError', - error: new MonthlyConversationLimitError('Service Quota Exceeded'), - }, - { - name: 'FeatureDevServiceErrorGuardrailsException', - error: new FeatureDevServiceError( - i18n('AWS.amazonq.featureDev.error.codeGen.default'), - 'GuardrailsException' - ), - }, - { - name: 'FeatureDevServiceErrorEmptyPatchException', - error: new FeatureDevServiceError( - i18n('AWS.amazonq.featureDev.error.throttling'), - 'EmptyPatchException' - ), - }, - { - name: 'FeatureDevServiceErrorThrottlingException', - error: new FeatureDevServiceError( - i18n('AWS.amazonq.featureDev.error.codeGen.default'), - 'ThrottlingException' - ), - }, - { name: 'UploadCodeError', error: new UploadCodeError('403: Forbiden') }, - { name: 'UserMessageNotFoundError', error: new UserMessageNotFoundError() }, - { name: 'TabIdNotFoundError', error: new TabIdNotFoundError() }, - { name: 'PrepareRepoFailedError', error: new PrepareRepoFailedError() }, - { name: 'PromptRefusalException', error: new PromptRefusalException() }, - { name: 'ZipFileError', error: new ZipFileError() }, - { name: 'CodeIterationLimitError', error: new CodeIterationLimitError() }, - { name: 'UploadURLExpired', error: new UploadURLExpired() }, - { name: 'NoChangeRequiredException', error: new NoChangeRequiredException() }, - { name: 'default', error: new ToolkitError('Default', { code: 'Default' }) }, - ] - - async function fireChatMessage(session: Session) { - const getSessionStub = sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session) - - controllerSetup.emitters.processHumanChatMessage.fire({ - tabID, - conversationID, - message: 'test message', - }) - - /** - * Wait until the controller has time to process the event - * Sessions should be called twice: - * 1. When the session getWorkspaceRoot is called - * 2. When the controller processes preloader - */ - await waitUntil(() => { - return Promise.resolve(getSessionStub.callCount > 1) - }, {}) - } - - describe('onCodeGeneration', function () { - let session: any - let sendMetricDataTelemetrySpy: sinon.SinonStub - - async function verifyException(error: ToolkitError) { - sinon.stub(session, 'send').throws(error) - - await fireChatMessage(session) - await verifyMetricsCalled() - assert.ok( - sendMetricDataTelemetrySpy.calledWith( - MetricDataOperationName.StartCodeGeneration, - MetricDataResult.Success - ) - ) - const metricResult = getMetricResult(error) - assert.ok( - sendMetricDataTelemetrySpy.calledWith(MetricDataOperationName.EndCodeGeneration, metricResult) - ) - } - - async function verifyMetricsCalled() { - await waitUntil(() => Promise.resolve(sendMetricDataTelemetrySpy.callCount >= 2), {}) - } - - async function verifyMessage( - expectedMessage: string, - type: MessengerTypes, - remainingIterations?: number, - totalIterations?: number - ) { - sinon.stub(session, 'send').resolves() - sinon.stub(session, 'sendLinesOfCodeGeneratedTelemetry').resolves() // Avoid sending extra telemetry - const sendAnswerSpy = sinon.stub(controllerSetup.messenger, 'sendAnswer') - sinon.stub(session.state, 'codeGenerationRemainingIterationCount').value(remainingIterations) - sinon.stub(session.state, 'codeGenerationTotalIterationCount').value(totalIterations) - - await fireChatMessage(session) - await verifyMetricsCalled() - - assert.ok( - sendAnswerSpy.calledWith({ - type, - tabID, - message: expectedMessage, - }) - ) - } - - beforeEach(async () => { - session = await createCodeGenState() - sinon.stub(session, 'preloader').resolves() - sendMetricDataTelemetrySpy = sinon.stub(session, 'sendMetricDataTelemetry') - }) - - it('sends success operation telemetry', async () => { - sinon.stub(session, 'send').resolves() - sinon.stub(session, 'sendLinesOfCodeGeneratedTelemetry').resolves() // Avoid sending extra telemetry - - await fireChatMessage(session) - await verifyMetricsCalled() - - assert.ok( - sendMetricDataTelemetrySpy.calledWith( - MetricDataOperationName.StartCodeGeneration, - MetricDataResult.Success - ) - ) - assert.ok( - sendMetricDataTelemetrySpy.calledWith( - MetricDataOperationName.EndCodeGeneration, - MetricDataResult.Success - ) - ) - }) - - for (const { name, error } of runs) { - it(`sends failure operation telemetry on ${name}`, async () => { - await verifyException(error) - }) - } - - // Using 3 to avoid spamming the tests - for (let remainingIterations = 0; remainingIterations <= 3; remainingIterations++) { - it(`verifies add code messages for remaining iterations at ${remainingIterations}`, async () => { - const totalIterations = 10 - const expectedMessage = (() => { - if (remainingIterations > 2) { - return 'Would you like me to add this code to your project, or provide feedback for new code?' - } else if (remainingIterations > 0) { - return `Would you like me to add this code to your project, or provide feedback for new code? You have ${remainingIterations} out of ${totalIterations} code generations left.` - } else { - return 'Would you like me to add this code to your project?' - } - })() - await verifyMessage(expectedMessage, 'answer', remainingIterations, totalIterations) - }) - } - - for (let remainingIterations = -1; remainingIterations <= 3; remainingIterations++) { - let remaining: number | undefined = remainingIterations - if (remainingIterations < 0) { - remaining = undefined - } - it(`verifies messages after cancellation for remaining iterations at ${remaining !== undefined ? remaining : 'undefined'}`, async () => { - const totalIterations = 10 - const expectedMessage = (() => { - if (remaining === undefined || remaining > 2) { - return 'I stopped generating your code. If you want to continue working on this task, provide another description.' - } else if (remaining > 0) { - return `I stopped generating your code. If you want to continue working on this task, provide another description. You have ${remaining} out of ${totalIterations} code generations left.` - } else { - return "I stopped generating your code. You don't have more iterations left, however, you can start a new session." - } - })() - session.state.tokenSource.cancel() - await verifyMessage( - expectedMessage, - 'answer-part', - remaining, - remaining === undefined ? undefined : totalIterations - ) - }) - } - }) - - describe('processErrorChatMessage', function () { - function createTestErrorMessage(message: string) { - return createUserFacingErrorMessage(`${featureName} request failed: ${message}`) - } - - async function verifyException(error: ToolkitError) { - sinon.stub(session, 'preloader').throws(error) - const sendAnswerSpy = sinon.stub(controllerSetup.messenger, 'sendAnswer') - const sendErrorMessageSpy = sinon.stub(controllerSetup.messenger, 'sendErrorMessage') - const sendMonthlyLimitErrorSpy = sinon.stub(controllerSetup.messenger, 'sendMonthlyLimitError') - - await fireChatMessage(session) - - switch (error.constructor.name) { - case ContentLengthError.name: - assert.ok( - sendAnswerSpy.calledWith({ - type: 'answer', - tabID, - message: error.message + messageWithConversationId(session?.conversationIdUnsafe), - canBeVoted: true, - }) - ) - break - case MonthlyConversationLimitError.name: - assert.ok(sendMonthlyLimitErrorSpy.calledWith(tabID)) - break - case FeatureDevServiceError.name: - case UploadCodeError.name: - case UserMessageNotFoundError.name: - case TabIdNotFoundError.name: - case PrepareRepoFailedError.name: - assert.ok( - sendErrorMessageSpy.calledWith( - createTestErrorMessage(error.message), - tabID, - session?.retries, - session?.conversationIdUnsafe - ) - ) - break - case PromptRefusalException.name: - case ZipFileError.name: - assert.ok( - sendErrorMessageSpy.calledWith( - createTestErrorMessage(error.message), - tabID, - 0, - session?.conversationIdUnsafe, - true - ) - ) - break - case NoChangeRequiredException.name: - case CodeIterationLimitError.name: - case UploadURLExpired.name: - assert.ok( - sendAnswerSpy.calledWith({ - type: 'answer', - tabID, - message: error.message, - canBeVoted: true, - }) - ) - break - default: - assert.ok( - sendErrorMessageSpy.calledWith( - i18n('AWS.amazonq.featureDev.error.codeGen.default'), - tabID, - session?.retries, - session?.conversationIdUnsafe, - true - ) - ) - break - } - } - - for (const run of runs) { - it(`should handle ${run.name}`, async function () { - await verifyException(run.error) - }) - } - }) - }) - - describe('stopResponse', () => { - it('should emit ui_click telemetry with elementId amazonq_stopCodeGeneration', async () => { - const getSessionStub = sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session) - controllerSetup.emitters.stopResponse.fire({ tabID, conversationID }) - await waitUntil(() => { - return Promise.resolve(getSessionStub.callCount > 0) - }, {}) - assertTelemetry('ui_click', { elementId: 'amazonq_stopCodeGeneration' }) - }) - }) - - describe('closeSession', async () => { - async function closeSessionClicked() { - const getSessionStub = sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session) - - controllerSetup.emitters.followUpClicked.fire({ - tabID, - followUp: { - type: FollowUpTypes.CloseSession, - }, - }) - - // Wait until the controller has time to process the event - await waitUntil(() => { - return Promise.resolve(getSessionStub.callCount > 0) - }, {}) - } - - it('end chat telemetry is sent', async () => { - await closeSessionClicked() - - assertTelemetry('amazonq_endChat', { amazonqConversationId: conversationID, result: 'Succeeded' }) - }) - }) -}) diff --git a/packages/core/src/test/amazonqFeatureDev/session/sessionState.test.ts b/packages/core/src/test/amazonqFeatureDev/session/sessionState.test.ts deleted file mode 100644 index 2d68654ee00..00000000000 --- a/packages/core/src/test/amazonqFeatureDev/session/sessionState.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import * as vscode from 'vscode' -import assert from 'assert' -import sinon from 'sinon' -import { - MockCodeGenState, - FeatureDevPrepareCodeGenState, - FeatureDevCodeGenState, -} from '../../../amazonqFeatureDev/session/sessionState' -import { ToolkitError } from '../../../shared/errors' -import * as crypto from '../../../shared/crypto' -import { createMockSessionStateAction } from '../../amazonq/utils' - -import { createTestContext, setupTestHooks } from '../../amazonq/session/testSetup' - -describe('sessionStateFeatureDev', () => { - const context = createTestContext() - setupTestHooks(context) - - describe('MockCodeGenState', () => { - it('loops forever in the same state', async () => { - sinon.stub(crypto, 'randomUUID').returns('upload-id' as ReturnType<(typeof crypto)['randomUUID']>) - const testAction = createMockSessionStateAction() - const state = new MockCodeGenState(context.testConfig, context.tabId) - const result = await state.interact(testAction) - - assert.deepStrictEqual(result, { - nextState: state, - interaction: {}, - }) - }) - }) - - describe('FeatureDevPrepareCodeGenState', () => { - it('error when failing to prepare repo information', async () => { - sinon.stub(vscode.workspace, 'findFiles').throws() - context.testMocks.createUploadUrl!.resolves({ uploadId: '', uploadUrl: '' }) - const testAction = createMockSessionStateAction() - - await assert.rejects(() => { - return new FeatureDevPrepareCodeGenState(context.testConfig, [], [], [], context.tabId, 0).interact( - testAction - ) - }) - }) - }) - - describe('FeatureDevCodeGenState', () => { - it('transitions to FeatureDevPrepareCodeGenState when codeGenerationStatus ready ', async () => { - context.testMocks.getCodeGeneration!.resolves({ - codeGenerationStatus: { status: 'Complete' }, - codeGenerationRemainingIterationCount: 2, - codeGenerationTotalIterationCount: 3, - }) - - context.testMocks.exportResultArchive!.resolves({ newFileContents: [], deletedFiles: [], references: [] }) - - const testAction = createMockSessionStateAction() - const state = new FeatureDevCodeGenState(context.testConfig, [], [], [], context.tabId, 0, {}, 2, 3) - const result = await state.interact(testAction) - - const nextState = new FeatureDevPrepareCodeGenState( - context.testConfig, - [], - [], - [], - context.tabId, - 1, - 2, - 3, - undefined - ) - - assert.deepStrictEqual(result.nextState?.deletedFiles, nextState.deletedFiles) - assert.deepStrictEqual(result.nextState?.filePaths, result.nextState?.filePaths) - assert.deepStrictEqual(result.nextState?.references, result.nextState?.references) - }) - - it('fails when codeGenerationStatus failed ', async () => { - context.testMocks.getCodeGeneration!.rejects(new ToolkitError('Code generation failed')) - const testAction = createMockSessionStateAction() - const state = new FeatureDevCodeGenState(context.testConfig, [], [], [], context.tabId, 0, {}) - try { - await state.interact(testAction) - assert.fail('failed code generations should throw an error') - } catch (e: any) { - assert.deepStrictEqual(e.message, 'Code generation failed') - } - }) - }) -}) diff --git a/packages/core/src/test/index.ts b/packages/core/src/test/index.ts index 9a01973e26d..c682b1f367e 100644 --- a/packages/core/src/test/index.ts +++ b/packages/core/src/test/index.ts @@ -22,6 +22,5 @@ export { getTestWorkspaceFolder } from '../testInteg/integrationTestsUtilities' export * from './codewhisperer/testUtil' export * from './credentials/testUtil' export * from './testUtil' -export * from './amazonq/utils' export * from './fake/mockFeatureConfigData' export * from './shared/ui/testUtils' diff --git a/packages/core/src/testInteg/perf/prepareRepoData.test.ts b/packages/core/src/testInteg/perf/prepareRepoData.test.ts deleted file mode 100644 index c1ba1df1223..00000000000 --- a/packages/core/src/testInteg/perf/prepareRepoData.test.ts +++ /dev/null @@ -1,84 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -import assert from 'assert' -import * as sinon from 'sinon' -import { WorkspaceFolder } from 'vscode' -import { getEqualOSTestOptions, performanceTest } from '../../shared/performance/performance' -import { createTestWorkspace } from '../../test/testUtil' -import { prepareRepoData, TelemetryHelper } from '../../amazonqFeatureDev' -import { AmazonqCreateUpload, fs, getRandomString } from '../../shared' -import { Span } from '../../shared/telemetry' -import { FileSystem } from '../../shared/fs/fs' -import { getFsCallsUpperBound } from './utilities' - -type resultType = { - zipFileBuffer: Buffer - zipFileChecksum: string -} - -type setupResult = { - workspace: WorkspaceFolder - fsSpy: sinon.SinonSpiedInstance - numFiles: number - fileSize: number -} - -function performanceTestWrapper(numFiles: number, fileSize: number) { - return performanceTest( - getEqualOSTestOptions({ - userCpuUsage: 200, - systemCpuUsage: 35, - heapTotal: 20, - }), - `handles ${numFiles} files of size ${fileSize} bytes`, - function () { - const telemetry = new TelemetryHelper() - return { - setup: async () => { - const fsSpy = sinon.spy(fs) - const workspace = await createTestWorkspace(numFiles, { - fileNamePrefix: 'file', - fileContent: getRandomString(fileSize), - fileNameSuffix: '.md', - }) - return { workspace, fsSpy, numFiles, fileSize } - }, - execute: async (setup: setupResult) => { - return await prepareRepoData( - [setup.workspace.uri.fsPath], - [setup.workspace], - { - record: () => {}, - } as unknown as Span, - { telemetry } - ) - }, - verify: async (setup: setupResult, result: resultType) => { - verifyResult(setup, result, telemetry, numFiles * fileSize) - }, - } - } - ) -} - -function verifyResult(setup: setupResult, result: resultType, telemetry: TelemetryHelper, expectedSize: number): void { - assert.ok(result) - assert.strictEqual(Buffer.isBuffer(result.zipFileBuffer), true) - assert.strictEqual(telemetry.repositorySize, expectedSize) - assert.strictEqual(result.zipFileChecksum.length, 44) - assert.ok(getFsCallsUpperBound(setup.fsSpy) <= setup.numFiles * 8, 'total system calls should be under 8 per file') -} - -describe('prepareRepoData', function () { - describe('Performance Tests', function () { - afterEach(function () { - sinon.restore() - }) - performanceTestWrapper(10, 1000) - performanceTestWrapper(50, 500) - performanceTestWrapper(100, 100) - performanceTestWrapper(250, 10) - }) -}) diff --git a/packages/core/src/testInteg/perf/registerNewFiles.test.ts b/packages/core/src/testInteg/perf/registerNewFiles.test.ts deleted file mode 100644 index 716e79d4e48..00000000000 --- a/packages/core/src/testInteg/perf/registerNewFiles.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -import assert from 'assert' -import sinon from 'sinon' -import * as vscode from 'vscode' -import { featureDevScheme } from '../../amazonqFeatureDev' -import { getEqualOSTestOptions, performanceTest } from '../../shared/performance/performance' -import { getTestWorkspaceFolder } from '../integrationTestsUtilities' -import { VirtualFileSystem } from '../../shared' -import { registerNewFiles } from '../../amazonq/util/files' -import { NewFileInfo, NewFileZipContents } from '../../amazonq' - -interface SetupResult { - workspace: vscode.WorkspaceFolder - fileContents: NewFileZipContents[] - vfsSpy: sinon.SinonSpiedInstance - vfs: VirtualFileSystem -} - -function getFileContents(numFiles: number, fileSize: number): NewFileZipContents[] { - return Array.from({ length: numFiles }, (_, i) => { - return { - zipFilePath: `test-path-${i}`, - fileContent: 'x'.repeat(fileSize), - } - }) -} - -function performanceTestWrapper(label: string, numFiles: number, fileSize: number) { - const conversationId = 'test-conversation' - return performanceTest( - getEqualOSTestOptions({ - userCpuUsage: 300, - systemCpuUsage: 35, - heapTotal: 20, - }), - label, - function () { - return { - setup: async () => { - const testWorkspaceUri = vscode.Uri.file(getTestWorkspaceFolder()) - const fileContents = getFileContents(numFiles, fileSize) - const vfs = new VirtualFileSystem() - const vfsSpy = sinon.spy(vfs) - - return { - workspace: { - uri: testWorkspaceUri, - name: 'test-workspace', - index: 0, - }, - fileContents: fileContents, - vfsSpy: vfsSpy, - vfs: vfs, - } - }, - execute: async (setup: SetupResult) => { - return registerNewFiles( - setup.vfs, - setup.fileContents, - 'test-upload-id', - [setup.workspace], - conversationId, - featureDevScheme - ) - }, - verify: async (setup: SetupResult, result: NewFileInfo[]) => { - assert.strictEqual(result.length, numFiles) - assert.ok( - setup.vfsSpy.registerProvider.callCount <= numFiles, - 'only register each file once in vfs' - ) - }, - } - } - ) -} - -describe('registerNewFiles', function () { - describe('performance tests', function () { - performanceTestWrapper('1x10MB', 1, 10000) - performanceTestWrapper('10x1000B', 10, 1000) - performanceTestWrapper('100x100B', 100, 100) - performanceTestWrapper('1000x10B', 1000, 10) - performanceTestWrapper('10000x1B', 10000, 1) - }) -}) From 0bfc33839a694d2a6da86f19829a9811a044b297 Mon Sep 17 00:00:00 2001 From: Laxman Reddy <141967714+laileni-aws@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:43:26 -0700 Subject: [PATCH 63/69] fix(auth): Fix for SSO Profile Role Chaining Regression (#7764) ## Github Issue #6902 ## Problem AWS Toolkit version 3.47.0 introduced a regression where profiles using `source_profile` for role chaining fail to authenticate when the source profile uses SSO credentials. Users get an "InvalidClientTokenId: The security token included in the request is invalid" error. ## Root Cause The issue was introduced in commit 6f6a8c2 (Feb 13, 2025) which refactored the authentication code to remove deprecated AWS SDK dependencies. The new implementation in `makeSharedIniFileCredentialsProvider` method incorrectly assumed that the source profile would have static credentials (aws_access_key_id and aws_secret_access_key) directly in the profile data. When the source profile uses SSO, these static credentials don't exist in the profile data - they need to be obtained by calling the SSO service first. ## Solution The fix modifies the `makeSharedIniFileCredentialsProvider` method in `packages/core/src/auth/providers/sharedCredentialsProvider.ts` to: 1. Check if the source profile already has resolved credentials (from `patchSourceCredentials`) 2. If not, create a new `SharedCredentialsProvider` instance for the source profile and resolve its credentials dynamically 3. Use those resolved credentials to assume the role via STS This ensures that SSO profiles can be used as source profiles for role assumption. ## Changed Files - `packages/core/src/auth/providers/sharedCredentialsProvider.ts` - Fixed the credential resolution logic - `packages/core/src/test/auth/providers/sharedCredentialsProvider.roleChaining.test.ts` - Added tests to verify the fix ## Testing The fix includes unit tests that verify: 1. Role chaining from SSO profiles works correctly 2. Role chaining from SSO profiles with MFA works correctly ## Configuration Example This fix enables configurations like: ```ini [sso-session aws1_session] sso_start_url = https://example.awsapps.com/start sso_region = us-east-1 sso_registration_scopes = sso:account:access [profile Landing] sso_session = aws1_session sso_account_id = 111111111111 sso_role_name = Landing region = us-east-1 [profile dev] region = us-east-1 role_arn = arn:aws:iam::123456789012:role/dev source_profile = Landing ``` Where `dev` profile assumes a role using credentials from the SSO-based `Landing` profile. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- .../providers/sharedCredentialsProvider.ts | 26 ++++-- .../sharedCredentialsProvider.test.ts | 79 +++++++++++++++++++ 2 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 packages/core/src/test/auth/providers/sharedCredentialsProvider.test.ts diff --git a/packages/core/src/auth/providers/sharedCredentialsProvider.ts b/packages/core/src/auth/providers/sharedCredentialsProvider.ts index 717a151a3af..407db4a717e 100644 --- a/packages/core/src/auth/providers/sharedCredentialsProvider.ts +++ b/packages/core/src/auth/providers/sharedCredentialsProvider.ts @@ -406,12 +406,28 @@ export class SharedCredentialsProvider implements CredentialsProvider { `auth: Profile ${this.profileName} is missing source_profile for role assumption` ) } - // Use source profile to assume IAM role based on role ARN provided. + + // Check if we already have resolved credentials from patchSourceCredentials const sourceProfile = iniData[profile.source_profile!] - const stsClient = new DefaultStsClient(this.getDefaultRegion() ?? 'us-east-1', { - accessKeyId: sourceProfile.aws_access_key_id!, - secretAccessKey: sourceProfile.aws_secret_access_key!, - }) + let sourceCredentials: AWS.Credentials + + if (sourceProfile.aws_access_key_id && sourceProfile.aws_secret_access_key) { + // Source credentials have already been resolved + sourceCredentials = { + accessKeyId: sourceProfile.aws_access_key_id, + secretAccessKey: sourceProfile.aws_secret_access_key, + sessionToken: sourceProfile.aws_session_token, + } + } else { + // Source profile needs credential resolution - this should have been handled by patchSourceCredentials + // but if not, we need to resolve it here + const sourceProvider = new SharedCredentialsProvider(profile.source_profile!, this.sections) + sourceCredentials = await sourceProvider.getCredentials() + } + + // Use source credentials to assume IAM role based on role ARN provided. + const stsClient = new DefaultStsClient(this.getDefaultRegion() ?? 'us-east-1', sourceCredentials) + // Prompt for MFA Token if needed. const assumeRoleReq = { RoleArn: profile.role_arn, diff --git a/packages/core/src/test/auth/providers/sharedCredentialsProvider.test.ts b/packages/core/src/test/auth/providers/sharedCredentialsProvider.test.ts new file mode 100644 index 00000000000..1884e16e984 --- /dev/null +++ b/packages/core/src/test/auth/providers/sharedCredentialsProvider.test.ts @@ -0,0 +1,79 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import { SharedCredentialsProvider } from '../../../auth/providers/sharedCredentialsProvider' +import { createTestSections } from '../../credentials/testUtil' +import { DefaultStsClient } from '../../../shared/clients/stsClient' +import { oneDay } from '../../../shared/datetime' +import sinon from 'sinon' +import { SsoAccessTokenProvider } from '../../../auth/sso/ssoAccessTokenProvider' +import { SsoClient } from '../../../auth/sso/clients' + +describe('SharedCredentialsProvider - Role Chaining with SSO', function () { + let sandbox: sinon.SinonSandbox + + beforeEach(function () { + sandbox = sinon.createSandbox() + }) + + afterEach(function () { + sandbox.restore() + }) + + it('should handle role chaining from SSO profile', async function () { + // Mock the SSO authentication + sandbox.stub(SsoAccessTokenProvider.prototype, 'getToken').resolves({ + accessToken: 'test-token', + expiresAt: new Date(Date.now() + oneDay), + }) + + // Mock SSO getRoleCredentials + sandbox.stub(SsoClient.prototype, 'getRoleCredentials').resolves({ + accessKeyId: 'sso-access-key', + secretAccessKey: 'sso-secret-key', + sessionToken: 'sso-session-token', + expiration: new Date(Date.now() + oneDay), + }) + + // Mock STS assumeRole + sandbox.stub(DefaultStsClient.prototype, 'assumeRole').callsFake(async (request) => { + assert.strictEqual(request.RoleArn, 'arn:aws:iam::123456789012:role/dev') + return { + Credentials: { + AccessKeyId: 'assumed-access-key', + SecretAccessKey: 'assumed-secret-key', + SessionToken: 'assumed-session-token', + Expiration: new Date(Date.now() + oneDay), + }, + } + }) + + const sections = await createTestSections(` + [sso-session aws1_session] + sso_start_url = https://example.awsapps.com/start + sso_region = us-east-1 + sso_registration_scopes = sso:account:access + + [profile Landing] + sso_session = aws1_session + sso_account_id = 111111111111 + sso_role_name = Landing + region = us-east-1 + + [profile dev] + region = us-east-1 + role_arn = arn:aws:iam::123456789012:role/dev + source_profile = Landing + `) + + const provider = new SharedCredentialsProvider('dev', sections) + const credentials = await provider.getCredentials() + + assert.strictEqual(credentials.accessKeyId, 'assumed-access-key') + assert.strictEqual(credentials.secretAccessKey, 'assumed-secret-key') + assert.strictEqual(credentials.sessionToken, 'assumed-session-token') + }) +}) From 6ac1207c792c8d72408159145dffee177039ab12 Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Fri, 25 Jul 2025 12:35:32 -0700 Subject: [PATCH 64/69] config(amazonq): codewhisperer endpoint via settings.json (#7761) ## Problem ## Solution allow devs to configure Q endpoint via vscode settings.json ``` "aws.dev.codewhispererService": { "endpoint": "https://codewhisperer/endpoint/", "region": "us-east-1" } ``` --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/amazonq/src/lsp/client.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index e56a4a784b6..fa89f5f2ba4 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -39,6 +39,7 @@ import { getClientId, extensionVersion, isSageMaker, + DevSettings, } from 'aws-core-vscode/shared' import { processUtils } from 'aws-core-vscode/shared' import { activate } from './chat/activation' @@ -129,6 +130,15 @@ export async function startLanguageServer( await validateNodeExe(executable, resourcePaths.lsp, argv, logger) + const endpointOverride = DevSettings.instance.get('codewhispererService', {}).endpoint ?? undefined + const textDocSection = { + inlineEditSupport: Experiments.instance.get('amazonqLSPNEP', true), + } as any + + if (endpointOverride) { + textDocSection.endpointOverride = endpointOverride + } + // Options to control the language client const clientOptions: LanguageClientOptions = { // Register the server for json documents @@ -177,9 +187,7 @@ export async function startLanguageServer( showLogs: true, }, textDocument: { - inlineCompletionWithReferences: { - inlineEditSupport: Experiments.instance.get('amazonqLSPNEP', true), - }, + inlineCompletionWithReferences: textDocSection, }, }, contextConfiguration: { From f36023f7d49a51d0f6041f38df859b887d9d750b Mon Sep 17 00:00:00 2001 From: Bryce Ito Date: Mon, 28 Jul 2025 10:35:21 -0700 Subject: [PATCH 65/69] fix(auth): Apply static workspace ID for Eclipse Che instances (#7614) ## Problem Eclipse Che-based workspaces on remote compute will change their hostname if the backing compute changes, thus requiring a reauth. ## Solution Swap to keying off the Che workspace ID, which should be static for specific workspaces --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/core/src/shared/vscode/env.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/core/src/shared/vscode/env.ts b/packages/core/src/shared/vscode/env.ts index 5ee891cc7d3..abd9c58ae2d 100644 --- a/packages/core/src/shared/vscode/env.ts +++ b/packages/core/src/shared/vscode/env.ts @@ -307,6 +307,15 @@ export async function getMachineId(): Promise { // TODO: use `vscode.env.machineId` instead? return 'browser' } + // Eclipse Che-based envs (backing compute rotates, not classified as a web instance) + // TODO: use `vscode.env.machineId` instead? + if (process.env.CHE_WORKSPACE_ID) { + return process.env.CHE_WORKSPACE_ID + } + // RedHat Dev Workspaces (run some VSC web variant) + if (process.env.DEVWORKSPACE_ID) { + return process.env.DEVWORKSPACE_ID + } const proc = new ChildProcess('hostname', [], { collect: true, logging: 'no' }) // TODO: check exit code. return (await proc.run()).stdout.trim() ?? 'unknown-host' From 3b852696869d8095f6a250df0cb800e12217a0de Mon Sep 17 00:00:00 2001 From: chungjac Date: Mon, 28 Jul 2025 11:39:41 -0700 Subject: [PATCH 66/69] telemetry(amazonq): flare is now source of truth for metrics (#7768) ## Problem In VSC, we check that the metric name must be in [aws-toolkit-common](https://github.com/aws/aws-toolkit-common) before emitting the metric. Therefore when we want to add a new metric, the current process is: 1. Add new metric in aws-toolkit-common 2. Wait for version to increment (~1 hour) 3. Bump up toolkit-common version in VSC repo 4. Wait for next VSC release (up to 1 week) Only after steps 1-4, will we be actually emitting the new metric. JB, VS, and Eclipse do not have this dependency, and assume Flare is the source of truth for metrics ## Solution In VSC, Flare is now the source of truth for metrics instead of depending on aws-toolkit-common --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/amazonq/src/lsp/chat/messages.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index 7b3b130ff85..a95b99b442c 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -97,7 +97,7 @@ import { ViewDiffMessage, referenceLogText, } from 'aws-core-vscode/amazonq' -import { telemetry, TelemetryBase } from 'aws-core-vscode/telemetry' +import { telemetry } from 'aws-core-vscode/telemetry' import { isValidResponseError } from './error' import { decryptResponse, encryptRequest } from '../encryption' import { getCursorState } from '../utils' @@ -144,10 +144,13 @@ export function registerLanguageServerEventListener(languageClient: LanguageClie // This passes through metric data from LSP events to Toolkit telemetry with all fields from the LSP server languageClient.onTelemetry((e) => { const telemetryName: string = e.name - - if (telemetryName in telemetry) { - languageClient.info(`[VSCode Telemetry] Emitting ${telemetryName} telemetry: ${JSON.stringify(e.data)}`) - telemetry[telemetryName as keyof TelemetryBase].emit(e.data) + languageClient.info(`[VSCode Telemetry] Emitting ${telemetryName} telemetry: ${JSON.stringify(e.data)}`) + try { + // Flare is now the source of truth for metrics instead of depending on each IDE client and toolkit-common + const metric = (telemetry as any).getMetric(telemetryName) + metric?.emit(e.data) + } catch (error) { + languageClient.warn(`[VSCode Telemetry] Failed to emit ${telemetryName}: ${error}`) } }) } From f724fe9f2904d82aab691302fd2b14e72444f538 Mon Sep 17 00:00:00 2001 From: Laxman Reddy <141967714+laileni-aws@users.noreply.github.com> Date: Mon, 28 Jul 2025 12:30:01 -0700 Subject: [PATCH 67/69] refactor(amazonq): removing agentWalkThrough workflow (#7775) ## Notes: - Removing agentWalkThrough workflow form VSCode. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/amazonq/package.json | 17 +- .../amazonq/test/e2e/amazonq/explore.test.ts | 45 ---- packages/core/package.nls.json | 1 - .../ui/apps/amazonqCommonsConnector.ts | 7 +- .../core/src/amazonq/webview/ui/connector.ts | 4 - packages/core/src/amazonq/webview/ui/main.ts | 14 -- .../webview/ui/quickActions/generator.ts | 7 +- .../webview/ui/storages/tabsStorage.ts | 2 +- .../src/amazonq/webview/ui/tabs/constants.ts | 2 +- .../src/amazonq/webview/ui/tabs/generator.ts | 7 +- .../amazonq/webview/ui/walkthrough/agent.ts | 201 ------------------ packages/core/src/codewhisperer/activation.ts | 2 - .../codewhisperer/commands/basicCommands.ts | 15 -- 13 files changed, 8 insertions(+), 316 deletions(-) delete mode 100644 packages/amazonq/test/e2e/amazonq/explore.test.ts delete mode 100644 packages/core/src/amazonq/webview/ui/walkthrough/agent.ts diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index d791ba05af3..58161b6ffdb 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -524,22 +524,17 @@ "command": "aws.amazonq.walkthrough.show", "group": "1_help@1" }, - { - "command": "aws.amazonq.exploreAgents", - "when": "!aws.isSageMaker", - "group": "1_help@2" - }, { "command": "aws.amazonq.github", - "group": "1_help@3" + "group": "1_help@2" }, { "command": "aws.amazonq.aboutExtension", - "group": "1_help@4" + "group": "1_help@3" }, { "command": "aws.amazonq.viewLogs", - "group": "1_help@5" + "group": "1_help@4" } ], "aws.amazonq.submenu.securityIssueMoreActions": [ @@ -846,12 +841,6 @@ "title": "%AWS.amazonq.openChat%", "category": "%AWS.amazonq.title%" }, - { - "command": "aws.amazonq.exploreAgents", - "title": "%AWS.amazonq.exploreAgents%", - "category": "%AWS.amazonq.title%", - "enablement": "aws.codewhisperer.connected && !aws.isSageMaker" - }, { "command": "aws.amazonq.walkthrough.show", "title": "%AWS.amazonq.welcomeWalkthrough%" diff --git a/packages/amazonq/test/e2e/amazonq/explore.test.ts b/packages/amazonq/test/e2e/amazonq/explore.test.ts deleted file mode 100644 index 970d93d00bb..00000000000 --- a/packages/amazonq/test/e2e/amazonq/explore.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import assert from 'assert' -import sinon from 'sinon' -import { qTestingFramework } from './framework/framework' -import { Messenger } from './framework/messenger' - -describe('Amazon Q Explore page', function () { - let framework: qTestingFramework - let tab: Messenger - - beforeEach(() => { - framework = new qTestingFramework('agentWalkthrough', true, [], 0) - const welcomeTab = framework.getTabs()[0] - welcomeTab.clickInBodyButton('explore') - - // Find the new explore tab - const exploreTab = framework.findTab('Explore') - if (!exploreTab) { - assert.fail('Explore tab not found') - } - tab = exploreTab - }) - - afterEach(() => { - framework.removeTab(tab.tabID) - framework.dispose() - sinon.restore() - }) - - // TODO refactor page objects so we can associate clicking user guides with actual urls - // TODO test that clicking quick start changes the tab title, etc - it('should have correct button IDs', async () => { - const features = ['featuredev', 'testgen', 'doc', 'review', 'gumby'] - - for (const [index, feature] of features.entries()) { - const buttons = (tab.getStore().chatItems ?? [])[index].buttons ?? [] - assert.deepStrictEqual(buttons[0].id, `user-guide-${feature}`) - assert.deepStrictEqual(buttons[1].id, `quick-start-${feature}`) - } - }) -}) diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index 0a25550ec22..498a3583a00 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -354,7 +354,6 @@ "AWS.amazonq.security": "Code Issues", "AWS.amazonq.login": "Login", "AWS.amazonq.learnMore": "Learn More About Amazon Q", - "AWS.amazonq.exploreAgents": "Explore Agent Capabilities", "AWS.amazonq.welcomeWalkthrough": "Welcome Walkthrough", "AWS.amazonq.codewhisperer.title": "Amazon Q", "AWS.amazonq.toggleCodeSuggestion": "Toggle Auto-Suggestions", diff --git a/packages/core/src/amazonq/webview/ui/apps/amazonqCommonsConnector.ts b/packages/core/src/amazonq/webview/ui/apps/amazonqCommonsConnector.ts index 68983b6c188..ee20b9b0726 100644 --- a/packages/core/src/amazonq/webview/ui/apps/amazonqCommonsConnector.ts +++ b/packages/core/src/amazonq/webview/ui/apps/amazonqCommonsConnector.ts @@ -33,14 +33,12 @@ export interface CodeReference { export class Connector { private readonly sendMessageToExtension private readonly onWelcomeFollowUpClicked - private readonly onNewTab private readonly handleCommand private readonly sendStaticMessage constructor(props: ConnectorProps) { this.sendMessageToExtension = props.sendMessageToExtension this.onWelcomeFollowUpClicked = props.onWelcomeFollowUpClicked - this.onNewTab = props.onNewTab this.handleCommand = props.handleCommand this.sendStaticMessage = props.sendStaticMessages } @@ -61,10 +59,7 @@ export class Connector { } handleMessageReceive = async (messageData: any): Promise => { - if (messageData.command === 'showExploreAgentsView') { - this.onNewTab('agentWalkthrough') - return - } else if (messageData.command === 'review') { + if (messageData.command === 'review') { // tabID does not exist when calling from QuickAction Menu bar this.handleCommand({ command: '/review' }, '') return diff --git a/packages/core/src/amazonq/webview/ui/connector.ts b/packages/core/src/amazonq/webview/ui/connector.ts index cc1b010375a..97821fc842f 100644 --- a/packages/core/src/amazonq/webview/ui/connector.ts +++ b/packages/core/src/amazonq/webview/ui/connector.ts @@ -596,10 +596,6 @@ export class Connector { this.cwChatConnector.onCustomFormAction(tabId, action) } break - case 'agentWalkthrough': { - this.amazonqCommonsConnector.onCustomFormAction(tabId, action) - break - } } } } diff --git a/packages/core/src/amazonq/webview/ui/main.ts b/packages/core/src/amazonq/webview/ui/main.ts index 54696982ae0..c6df42f0566 100644 --- a/packages/core/src/amazonq/webview/ui/main.ts +++ b/packages/core/src/amazonq/webview/ui/main.ts @@ -32,7 +32,6 @@ import { DiffTreeFileInfo } from './diffTree/types' import { FeatureContext } from '../../../shared/featureConfig' import { tryNewMap } from '../../util/functionUtils' import { welcomeScreenTabData } from './walkthrough/welcome' -import { agentWalkthroughDataModel } from './walkthrough/agent' import { createClickTelemetry, createOpenAgentTelemetry } from './telemetry/actions' import { disclaimerAcknowledgeButtonId, disclaimerCard } from './texts/disclaimer' import { DetailedListSheetProps } from '@aws/mynah-ui/dist/components/detailed-list/detailed-list-sheet' @@ -783,19 +782,6 @@ export class WebviewUIHandler { this.postMessage(createClickTelemetry('amazonq-welcome-quick-start-button')) return } - case 'explore': { - const newTabId = this.mynahUI?.updateStore('', agentWalkthroughDataModel) - if (newTabId === undefined) { - this.mynahUI?.notify({ - content: uiComponentsTexts.noMoreTabsTooltip, - type: NotificationType.WARNING, - }) - return - } - this.tabsStorage.updateTabTypeFromUnknown(newTabId, 'agentWalkthrough') - this.postMessage(createClickTelemetry('amazonq-welcome-explore-button')) - return - } default: { this.connector?.onCustomFormAction(tabId, messageId, action, eventId) return diff --git a/packages/core/src/amazonq/webview/ui/quickActions/generator.ts b/packages/core/src/amazonq/webview/ui/quickActions/generator.ts index 0cc7740f2ec..4ca8b4cc10e 100644 --- a/packages/core/src/amazonq/webview/ui/quickActions/generator.ts +++ b/packages/core/src/amazonq/webview/ui/quickActions/generator.ts @@ -25,11 +25,6 @@ export class QuickActionGenerator { } public generateForTab(tabType: TabType): QuickActionCommandGroup[] { - // agentWalkthrough is static and doesn't have any quick actions - if (tabType === 'agentWalkthrough') { - return [] - } - // TODO: Update acc to UX const quickActionCommands = [ { @@ -101,7 +96,7 @@ export class QuickActionGenerator { ].filter((section) => section.commands.length > 0) const commandUnavailability: Record< - Exclude, + Exclude, { description: string unavailableItems: string[] diff --git a/packages/core/src/amazonq/webview/ui/storages/tabsStorage.ts b/packages/core/src/amazonq/webview/ui/storages/tabsStorage.ts index 92fa7c5a07e..2a803759fd0 100644 --- a/packages/core/src/amazonq/webview/ui/storages/tabsStorage.ts +++ b/packages/core/src/amazonq/webview/ui/storages/tabsStorage.ts @@ -4,7 +4,7 @@ */ export type TabStatus = 'free' | 'busy' | 'dead' -const TabTypes = ['cwc', 'gumby', 'review', 'agentWalkthrough', 'welcome', 'unknown'] as const +const TabTypes = ['cwc', 'gumby', 'review', 'welcome', 'unknown'] as const export type TabType = (typeof TabTypes)[number] export function isTabType(value: string): value is TabType { return (TabTypes as readonly string[]).includes(value) diff --git a/packages/core/src/amazonq/webview/ui/tabs/constants.ts b/packages/core/src/amazonq/webview/ui/tabs/constants.ts index ead70679b7f..0872b829a6a 100644 --- a/packages/core/src/amazonq/webview/ui/tabs/constants.ts +++ b/packages/core/src/amazonq/webview/ui/tabs/constants.ts @@ -44,7 +44,7 @@ export const commonTabData: TabTypeData = { contextCommands: [workspaceCommand], } -export const TabTypeDataMap: Record, TabTypeData> = { +export const TabTypeDataMap: Record, TabTypeData> = { unknown: commonTabData, cwc: commonTabData, gumby: { diff --git a/packages/core/src/amazonq/webview/ui/tabs/generator.ts b/packages/core/src/amazonq/webview/ui/tabs/generator.ts index 2331a0721c7..68b758d51cb 100644 --- a/packages/core/src/amazonq/webview/ui/tabs/generator.ts +++ b/packages/core/src/amazonq/webview/ui/tabs/generator.ts @@ -8,7 +8,6 @@ import { TabType } from '../storages/tabsStorage' import { FollowUpGenerator } from '../followUps/generator' import { QuickActionGenerator } from '../quickActions/generator' import { qChatIntroMessageForSMUS, TabTypeDataMap } from './constants' -import { agentWalkthroughDataModel } from '../walkthrough/agent' import { FeatureContext } from '../../../../shared/featureConfig' import { RegionProfile } from '../../../../codewhisperer/models/model' @@ -43,10 +42,6 @@ export class TabDataGenerator { taskName?: string, isSMUS?: boolean ): MynahUIDataModel { - if (tabType === 'agentWalkthrough') { - return agentWalkthroughDataModel - } - if (tabType === 'welcome') { return {} } @@ -86,7 +81,7 @@ export class TabDataGenerator { } private getContextCommands(tabType: TabType): QuickActionCommandGroup[] | undefined { - if (tabType === 'agentWalkthrough' || tabType === 'welcome') { + if (tabType === 'welcome') { return } diff --git a/packages/core/src/amazonq/webview/ui/walkthrough/agent.ts b/packages/core/src/amazonq/webview/ui/walkthrough/agent.ts deleted file mode 100644 index f4a5add7aa1..00000000000 --- a/packages/core/src/amazonq/webview/ui/walkthrough/agent.ts +++ /dev/null @@ -1,201 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { ChatItemContent, ChatItemType, MynahIcons, MynahUIDataModel } from '@aws/mynah-ui' - -function createdTabbedData(examples: string[], agent: string): ChatItemContent['tabbedContent'] { - const exampleText = examples.map((example) => `- ${example}`).join('\n') - return [ - { - label: 'Examples', - value: 'examples', - content: { - body: `**Example use cases:**\n${exampleText}\n\nEnter ${agent} in Q Chat to get started`, - }, - }, - ] -} - -export const agentWalkthroughDataModel: MynahUIDataModel = { - tabBackground: false, - compactMode: false, - tabTitle: 'Explore', - promptInputVisible: false, - tabHeaderDetails: { - icon: MynahIcons.ASTERISK, - title: 'Amazon Q Developer agents capabilities', - description: '', - }, - chatItems: [ - { - type: ChatItemType.ANSWER, - snapToTop: true, - hoverEffect: true, - body: `### Feature development -Implement features or make changes across your workspace, all from a single prompt. -`, - icon: MynahIcons.CODE_BLOCK, - footer: { - tabbedContent: createdTabbedData( - [ - '/dev update app.py to add a new api', - '/dev fix the error', - '/dev add a new button to sort by ', - ], - '/dev' - ), - }, - buttons: [ - { - status: 'clear', - id: `user-guide-featuredev`, - disabled: false, - text: 'Read user guide', - }, - { - status: 'main', - disabled: false, - flash: 'once', - fillState: 'hover', - icon: MynahIcons.RIGHT_OPEN, - id: 'quick-start-featuredev', - text: `Quick start with **/dev**`, - }, - ], - }, - { - type: ChatItemType.ANSWER, - hoverEffect: true, - body: `### Unit test generation -Automatically generate unit tests for your active file. -`, - icon: MynahIcons.BUG, - footer: { - tabbedContent: createdTabbedData( - ['Generate tests for specific functions', 'Generate tests for null and empty inputs'], - '/test' - ), - }, - buttons: [ - { - status: 'clear', - id: 'user-guide-testgen', - disabled: false, - text: 'Read user guide', - }, - { - status: 'main', - disabled: false, - flash: 'once', - fillState: 'hover', - icon: MynahIcons.RIGHT_OPEN, - id: 'quick-start-testgen', - text: `Quick start with **/test**`, - }, - ], - }, - { - type: ChatItemType.ANSWER, - hoverEffect: true, - body: `### Documentation generation -Create and update READMEs for better documented code. -`, - icon: MynahIcons.CHECK_LIST, - footer: { - tabbedContent: createdTabbedData( - [ - 'Generate new READMEs for your project', - 'Update existing READMEs with recent code changes', - 'Request specific changes to a README', - ], - '/doc' - ), - }, - buttons: [ - { - status: 'clear', - id: 'user-guide-doc', - disabled: false, - text: 'Read user guide', - }, - { - status: 'main', - disabled: false, - flash: 'once', - fillState: 'hover', - icon: MynahIcons.RIGHT_OPEN, - id: 'quick-start-doc', - text: `Quick start with **/doc**`, - }, - ], - }, - { - type: ChatItemType.ANSWER, - hoverEffect: true, - body: `### Code reviews -Review code for issues, then get suggestions to fix your code instantaneously. -`, - icon: MynahIcons.TRANSFORM, - footer: { - tabbedContent: createdTabbedData( - [ - 'Review code for security vulnerabilities and code quality issues', - 'Get detailed explanations about code issues', - 'Apply automatic code fixes to your files', - ], - '/review' - ), - }, - buttons: [ - { - status: 'clear', - id: 'user-guide-review', - disabled: false, - text: 'Read user guide', - }, - { - status: 'main', - disabled: false, - flash: 'once', - fillState: 'hover', - icon: MynahIcons.RIGHT_OPEN, - id: 'quick-start-review', - text: `Quick start with **/review**`, - }, - ], - }, - { - type: ChatItemType.ANSWER, - hoverEffect: true, - body: `### Transformation -Upgrade library and language versions in your codebase. -`, - icon: MynahIcons.TRANSFORM, - footer: { - tabbedContent: createdTabbedData( - ['Upgrade Java language and dependency versions', 'Convert embedded SQL code in Java apps'], - '/transform' - ), - }, - buttons: [ - { - status: 'clear', - id: 'user-guide-gumby', - disabled: false, - text: 'Read user guide', - }, - { - status: 'main', - disabled: false, - flash: 'once', - fillState: 'hover', - icon: MynahIcons.RIGHT_OPEN, - id: 'quick-start-gumby', - text: `Quick start with **/transform**`, - }, - ], - }, - ], -} diff --git a/packages/core/src/codewhisperer/activation.ts b/packages/core/src/codewhisperer/activation.ts index 1e73b640a1e..941156a0d2e 100644 --- a/packages/core/src/codewhisperer/activation.ts +++ b/packages/core/src/codewhisperer/activation.ts @@ -49,7 +49,6 @@ import { regenerateFix, ignoreAllIssues, focusIssue, - showExploreAgentsView, showCodeIssueGroupingQuickPick, selectRegionProfileCommand, } from './commands/basicCommands' @@ -301,7 +300,6 @@ export async function activate(context: ExtContext): Promise { vscode.window.registerWebviewViewProvider(ReferenceLogViewProvider.viewType, ReferenceLogViewProvider.instance), showReferenceLog.register(), showLogs.register(), - showExploreAgentsView.register(), vscode.languages.registerCodeLensProvider( [...CodeWhispererConstants.platformLanguageIds], ReferenceInlineProvider.instance diff --git a/packages/core/src/codewhisperer/commands/basicCommands.ts b/packages/core/src/codewhisperer/commands/basicCommands.ts index a8c21b86ce2..ec1b5ae6e63 100644 --- a/packages/core/src/codewhisperer/commands/basicCommands.ts +++ b/packages/core/src/codewhisperer/commands/basicCommands.ts @@ -60,7 +60,6 @@ import { SecurityIssueProvider } from '../service/securityIssueProvider' import { CodeWhispererSettings } from '../util/codewhispererSettings' import { closeDiff, getPatchedCode } from '../../shared/utilities/diffUtils' import { insertCommentAboveLine } from '../../shared/utilities/commentUtils' -import { DefaultAmazonQAppInitContext } from '../../amazonq/apps/initContext' import path from 'path' import { UserWrittenCodeTracker } from '../tracker/userWrittenCodeTracker' import { parsePatch } from 'diff' @@ -173,20 +172,6 @@ export const showLogs = Commands.declare( } ) -export const showExploreAgentsView = Commands.declare( - { id: 'aws.amazonq.exploreAgents', compositeKey: { 1: 'source' } }, - () => async (_: VsCodeCommandArg, source: CodeWhispererSource) => { - if (_ !== placeholder) { - source = 'ellipsesMenu' - } - - DefaultAmazonQAppInitContext.instance.getAppsToWebViewMessagePublisher().publish({ - sender: 'amazonqCore', - command: 'showExploreAgentsView', - }) - } -) - export const showIntroduction = Commands.declare('aws.amazonq.introduction', () => async () => { void openUrl(vscode.Uri.parse(CodeWhispererConstants.learnMoreUriGeneral)) }) From 0fcd624a9c7b32d79f1b771a2bc8d3d66f26db67 Mon Sep 17 00:00:00 2001 From: abhraina-aws Date: Mon, 28 Jul 2025 13:24:57 -0700 Subject: [PATCH 68/69] fix(amazonq): switch off the feature flag incase sagemaker is involved (#7777) ## Problem Sagemaker was showing show logs feature when it cant support it. ## Solution Added the check for sage maker. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- packages/amazonq/src/lsp/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index fa89f5f2ba4..d335dae40ef 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -184,7 +184,7 @@ export async function startLanguageServer( window: { notifications: true, showSaveFileDialog: true, - showLogs: true, + showLogs: isSageMaker() ? false : true, }, textDocument: { inlineCompletionWithReferences: textDocSection, From 0be5c10e004530ba6426c05cf6c5ba7e7876b052 Mon Sep 17 00:00:00 2001 From: Suraj Reddy Date: Tue, 29 Jul 2025 15:51:08 -0400 Subject: [PATCH 69/69] update package-lock.json --- package-lock.json | 36852 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 36852 insertions(+) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000000..fc3d52b78bc --- /dev/null +++ b/package-lock.json @@ -0,0 +1,36852 @@ +{ + "name": "root", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "root", + "version": "0.0.1", + "hasInstallScript": true, + "license": "Apache-2.0", + "workspaces": [ + "packages/core/src/web", + "packages/*", + "plugins/*" + ], + "dependencies": { + "@types/node": "^22.7.5", + "@types/selenium-webdriver": "^4.1.28", + "buffer": "^6.0.3", + "jaro-winkler": "^0.2.8", + "mocha": "^11.7.1", + "vscode-nls": "^5.2.0", + "vscode-nls-dev": "^4.0.4" + }, + "devDependencies": { + "@aws-toolkits/telemetry": "^1.0.329", + "@playwright/browser-chromium": "^1.43.1", + "@stylistic/eslint-plugin": "^2.11.0", + "@types/he": "^1.2.3", + "@types/jaro-winkler": "^0.2.4", + "@types/vscode": "^1.68.0", + "@types/vscode-webview": "^1.57.1", + "@types/webpack-env": "^1.18.5", + "@typescript-eslint/eslint-plugin": "^7.14.1", + "@typescript-eslint/parser": "^7.14.1", + "@vscode/codicons": "^0.0.33", + "@vscode/test-electron": "^2.3.8", + "@vscode/test-web": "^0.0.65", + "@vscode/vsce": "^2.19.0", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-aws-toolkits": "file:plugins/eslint-plugin-aws-toolkits", + "eslint-plugin-header": "^3.1.1", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-security-node": "^1.1.4", + "eslint-plugin-unicorn": "^54.0.0", + "husky": "^9.0.7", + "path-browserify": "^1.0.1", + "prettier": "^3.3.3", + "prettier-plugin-sh": "^0.14.0", + "pretty-quick": "^4.0.0", + "process": "^0.11.10", + "ts-node": "^10.9.1", + "typescript": "^5.0.4", + "util": "^0.12.5", + "vscode-extension-tester": "^8.16.2", + "webpack": "^5.95.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^4.15.2", + "webpack-merge": "^5.10.0" + }, + "engines": { + "vscode": "^1.68.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@amzn/amazon-q-developer-streaming-client": { + "resolved": "src.gen/@amzn/amazon-q-developer-streaming-client", + "link": true + }, + "node_modules/@amzn/codewhisperer-streaming": { + "resolved": "src.gen/@amzn/codewhisperer-streaming", + "link": true + }, + "node_modules/@amzn/sagemaker-client": { + "version": "1.0.0", + "resolved": "file:src.gen/@amzn/sagemaker-client/1.0.0.tgz", + "integrity": "sha512-rNMUzeACaCiIqR8aQo3G99xR+Qy6zhbGi9+6XRG5proUKetO3584dclmSnIUvDvzLWosFcl4GyP8tFqiahc6Jg==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.363.0", + "@aws-sdk/credential-provider-node": "3.363.0", + "@aws-sdk/middleware-host-header": "3.363.0", + "@aws-sdk/middleware-logger": "3.363.0", + "@aws-sdk/middleware-recursion-detection": "3.363.0", + "@aws-sdk/middleware-signing": "3.363.0", + "@aws-sdk/middleware-user-agent": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.363.0", + "@aws-sdk/util-user-agent-node": "3.363.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-retry": "^1.0.3", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.1.0", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.1.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.3", + "@smithy/util-utf8": "^1.0.1", + "@smithy/util-waiter": "^1.0.1", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/util": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/client-sso": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.363.0", + "@aws-sdk/middleware-logger": "3.363.0", + "@aws-sdk/middleware-recursion-detection": "3.363.0", + "@aws-sdk/middleware-user-agent": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.363.0", + "@aws-sdk/util-user-agent-node": "3.363.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.1", + "@smithy/middleware-retry": "^1.0.2", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.0.1", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.0.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.2", + "@smithy/util-utf8": "^1.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.363.0", + "@aws-sdk/middleware-logger": "3.363.0", + "@aws-sdk/middleware-recursion-detection": "3.363.0", + "@aws-sdk/middleware-user-agent": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.363.0", + "@aws-sdk/util-user-agent-node": "3.363.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.1", + "@smithy/middleware-retry": "^1.0.2", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.0.1", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.0.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.2", + "@smithy/util-utf8": "^1.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/client-sts": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/credential-provider-node": "3.363.0", + "@aws-sdk/middleware-host-header": "3.363.0", + "@aws-sdk/middleware-logger": "3.363.0", + "@aws-sdk/middleware-recursion-detection": "3.363.0", + "@aws-sdk/middleware-sdk-sts": "3.363.0", + "@aws-sdk/middleware-signing": "3.363.0", + "@aws-sdk/middleware-user-agent": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.363.0", + "@aws-sdk/util-user-agent-node": "3.363.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.1", + "@smithy/middleware-retry": "^1.0.1", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.1", + "@smithy/protocol-http": "^1.1.0", + "@smithy/smithy-client": "^1.0.2", + "@smithy/types": "^1.1.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.1", + "@smithy/util-utf8": "^1.0.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.363.0", + "@aws-sdk/credential-provider-process": "3.363.0", + "@aws-sdk/credential-provider-sso": "3.363.0", + "@aws-sdk/credential-provider-web-identity": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@smithy/credential-provider-imds": "^1.0.1", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.363.0", + "@aws-sdk/credential-provider-ini": "3.363.0", + "@aws-sdk/credential-provider-process": "3.363.0", + "@aws-sdk/credential-provider-sso": "3.363.0", + "@aws-sdk/credential-provider-web-identity": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@smithy/credential-provider-imds": "^1.0.1", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.363.0", + "@aws-sdk/token-providers": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-logger": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/token-providers": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/types": { + "version": "3.357.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/util-endpoints": { + "version": "3.357.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/types": "^1.1.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/abort-controller": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/config-resolver": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.2.0", + "@smithy/util-config-provider": "^1.1.0", + "@smithy/util-middleware": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/credential-provider-imds": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^1.1.0", + "@smithy/property-provider": "^1.2.0", + "@smithy/types": "^1.2.0", + "@smithy/url-parser": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/fetch-http-handler": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^1.2.0", + "@smithy/querystring-builder": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-base64": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/hash-node": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.2.0", + "@smithy/util-buffer-from": "^1.1.0", + "@smithy/util-utf8": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/invalid-dependency": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/is-array-buffer": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-content-length": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^1.2.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-endpoint": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/url-parser": "^1.1.0", + "@smithy/util-middleware": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-retry": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^1.2.0", + "@smithy/service-error-classification": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-middleware": "^1.1.0", + "@smithy/util-retry": "^1.1.0", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-serde": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/middleware-stack": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/node-config-provider": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^1.2.0", + "@smithy/shared-ini-file-loader": "^1.1.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/node-http-handler": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^1.1.0", + "@smithy/protocol-http": "^1.2.0", + "@smithy/querystring-builder": "^1.1.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/property-provider": { + "version": "1.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/protocol-http": { + "version": "1.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/querystring-builder": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.2.0", + "@smithy/util-uri-escape": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/querystring-parser": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/service-error-classification": { + "version": "1.1.0", + "license": "Apache-2.0", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/shared-ini-file-loader": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/smithy-client": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-stack": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-stream": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/types": { + "version": "1.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/url-parser": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^1.1.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-base64": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-body-length-browser": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-body-length-node": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-buffer-from": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-config-provider": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-defaults-mode-browser": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^1.2.0", + "@smithy/types": "^1.2.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-defaults-mode-node": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^1.1.0", + "@smithy/credential-provider-imds": "^1.1.0", + "@smithy/node-config-provider": "^1.1.0", + "@smithy/property-provider": "^1.2.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-hex-encoding": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-middleware": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-retry": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-stream": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^1.1.0", + "@smithy/node-http-handler": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-base64": "^1.1.0", + "@smithy/util-buffer-from": "^1.1.0", + "@smithy/util-hex-encoding": "^1.1.0", + "@smithy/util-utf8": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-uri-escape": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-utf8": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/@smithy/util-waiter": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^1.1.0", + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/fast-xml-parser": { + "version": "4.2.5", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@amzn/sagemaker-client/node_modules/uuid": { + "version": "8.3.2", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-api-gateway": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/client-sts": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-sdk-api-gateway": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-stream": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-api-gateway/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/client-sts": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-apprunner/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/client-sts": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.8", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudcontrol/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.682.0", + "@aws-sdk/client-sts": "3.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.6", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/client-sso": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.682.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/client-sts": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/core": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/core": "^2.4.8", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/signature-v4": "^4.2.0", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-middleware": "^3.0.7", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-stream": "^3.1.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-env": "3.679.0", + "@aws-sdk/credential-provider-http": "3.679.0", + "@aws-sdk/credential-provider-process": "3.679.0", + "@aws-sdk/credential-provider-sso": "3.682.0", + "@aws-sdk/credential-provider-web-identity": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.682.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.679.0", + "@aws-sdk/credential-provider-http": "3.679.0", + "@aws-sdk/credential-provider-ini": "3.682.0", + "@aws-sdk/credential-provider-process": "3.679.0", + "@aws-sdk/credential-provider-sso": "3.682.0", + "@aws-sdk/credential-provider-web-identity": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/token-providers": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.679.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/middleware-logger": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@smithy/core": "^2.4.8", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/token-providers": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.679.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/types": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/util-endpoints": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "@smithy/util-endpoints": "^2.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/types": "3.679.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.9", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.682.0", + "@aws-sdk/client-sts": "3.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/eventstream-serde-browser": "^3.0.10", + "@smithy/eventstream-serde-config-resolver": "^3.0.7", + "@smithy/eventstream-serde-node": "^3.0.9", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/client-sso": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.682.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/client-sts": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/core": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/core": "^2.4.8", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/signature-v4": "^4.2.0", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-middleware": "^3.0.7", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-stream": "^3.1.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-env": "3.679.0", + "@aws-sdk/credential-provider-http": "3.679.0", + "@aws-sdk/credential-provider-process": "3.679.0", + "@aws-sdk/credential-provider-sso": "3.682.0", + "@aws-sdk/credential-provider-web-identity": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.682.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.679.0", + "@aws-sdk/credential-provider-http": "3.679.0", + "@aws-sdk/credential-provider-ini": "3.682.0", + "@aws-sdk/credential-provider-process": "3.679.0", + "@aws-sdk/credential-provider-sso": "3.682.0", + "@aws-sdk/credential-provider-web-identity": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/token-providers": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.679.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-logger": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@smithy/core": "^2.4.8", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/token-providers": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.679.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/types": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-endpoints": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "@smithy/util-endpoints": "^2.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.679.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.682.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/types": "3.679.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.9", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/credential-provider-node": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/core": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.5", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-logger": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@smithy/core": "^3.1.5", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-endpoints": { + "version": "3.743.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "@smithy/util-endpoints": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/hash-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-retry": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/config-resolver": "^4.0.1", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-retry": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/core": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.5", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/middleware-logger": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@smithy/core": "^3.1.5", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/util-endpoints": { + "version": "3.743.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "@smithy/util-endpoints": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/hash-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/middleware-retry": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/config-resolver": "^4.0.1", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-retry": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@aws-sdk/core": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.5", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-env/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@aws-sdk/core": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.5", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.758.0", + "@aws-sdk/credential-provider-http": "3.758.0", + "@aws-sdk/credential-provider-ini": "3.758.0", + "@aws-sdk/credential-provider-process": "3.758.0", + "@aws-sdk/credential-provider-sso": "3.758.0", + "@aws-sdk/credential-provider-web-identity": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-node/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-node/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-node/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@aws-sdk/core": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.5", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-process/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/client-sso": "3.758.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/token-providers": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/core": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.5", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/nested-clients": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/abort-controller/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/credential-provider-imds/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/querystring-builder/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/querystring-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/querystring-parser/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/service-error-classification": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/service-error-classification/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream": { + "version": "4.1.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-stream/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-utf8/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-codecatalyst/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/credential-provider-node": "3.730.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.730.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/client-sso": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.730.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/core": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/core": "^3.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/signature-v4": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-stream": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/credential-provider-env": "3.730.0", + "@aws-sdk/credential-provider-http": "3.730.0", + "@aws-sdk/credential-provider-process": "3.730.0", + "@aws-sdk/credential-provider-sso": "3.730.0", + "@aws-sdk/credential-provider-web-identity": "3.730.0", + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.730.0", + "@aws-sdk/credential-provider-http": "3.730.0", + "@aws-sdk/credential-provider-ini": "3.730.0", + "@aws-sdk/credential-provider-process": "3.730.0", + "@aws-sdk/credential-provider-sso": "3.730.0", + "@aws-sdk/credential-provider-web-identity": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.730.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/token-providers": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-logger": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@smithy/core": "^3.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/nested-clients": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.730.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/token-providers": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/types": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-endpoints": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/abort-controller": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/config-resolver": { + "version": "4.1.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/core": { + "version": "3.5.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.0.8", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.6", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/hash-node": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/invalid-dependency": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-content-length": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-endpoint": { + "version": "4.1.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.5.3", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-retry": { + "version": "4.1.12", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/service-error-classification": "^4.0.5", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-serde": { + "version": "4.0.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-stack": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/node-config-provider": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/node-http-handler": { + "version": "4.0.6", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/property-provider": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/protocol-http": { + "version": "5.1.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/querystring-builder": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/querystring-parser": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/service-error-classification": { + "version": "4.0.5", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/signature-v4": { + "version": "5.1.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/smithy-client": { + "version": "4.4.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.5.3", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/types": { + "version": "4.3.1", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/url-parser": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.19", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.19", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.1.4", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-endpoints": { + "version": "3.0.6", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-middleware": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-retry": { + "version": "4.0.5", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.0.5", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-stream": { + "version": "4.2.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2": { + "version": "3.695.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/client-sts": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-sdk-ec2": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.8", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/client-sts": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-iam/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda": { + "version": "3.637.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.637.0", + "@aws-sdk/client-sts": "3.637.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.637.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/eventstream-serde-browser": "^3.0.6", + "@smithy/eventstream-serde-config-resolver": "^3.0.3", + "@smithy/eventstream-serde-node": "^3.0.5", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/client-sts": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-bucket-endpoint": "3.693.0", + "@aws-sdk/middleware-expect-continue": "3.693.0", + "@aws-sdk/middleware-flexible-checksums": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-location-constraint": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-sdk-s3": "3.693.0", + "@aws-sdk/middleware-ssec": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/signature-v4-multi-region": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@aws-sdk/xml-builder": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/eventstream-serde-browser": "^3.0.12", + "@smithy/eventstream-serde-config-resolver": "^3.0.9", + "@smithy/eventstream-serde-node": "^3.0.11", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-blob-browser": "^3.1.8", + "@smithy/hash-node": "^3.0.9", + "@smithy/hash-stream-node": "^3.1.8", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/md5-js": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-stream": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/client-sts": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.8", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/client-sts": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/client-sts": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.8", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ssm/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.637.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.637.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.637.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.637.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.637.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.637.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.637.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.637.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.635.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.4.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@aws-sdk/types": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@smithy/property-provider": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@smithy/types": { + "version": "4.3.1", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.635.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/credential-provider-env": "3.758.0", + "@aws-sdk/credential-provider-http": "3.758.0", + "@aws-sdk/credential-provider-process": "3.758.0", + "@aws-sdk/credential-provider-sso": "3.758.0", + "@aws-sdk/credential-provider-web-identity": "3.758.0", + "@aws-sdk/nested-clients": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/client-sso": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/core": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.5", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/client-sso": "3.758.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/token-providers": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/middleware-logger": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@smithy/core": "^3.1.5", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/token-providers": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/nested-clients": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/util-endpoints": { + "version": "3.743.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "@smithy/util-endpoints": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/hash-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-retry": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/querystring-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/service-error-classification": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/config-resolver": "^4.0.1", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-retry": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-stream": { + "version": "4.1.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.637.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-ini": "3.637.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.637.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.620.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.637.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.637.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.637.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.620.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.621.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.637.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.637.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/nested-clients": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/core": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.5", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/querystring-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-stream": { + "version": "4.1.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.730.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/credential-provider-cognito-identity": "3.730.0", + "@aws-sdk/credential-provider-env": "3.730.0", + "@aws-sdk/credential-provider-http": "3.730.0", + "@aws-sdk/credential-provider-ini": "3.730.0", + "@aws-sdk/credential-provider-node": "3.730.0", + "@aws-sdk/credential-provider-process": "3.730.0", + "@aws-sdk/credential-provider-sso": "3.730.0", + "@aws-sdk/credential-provider-web-identity": "3.730.0", + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/client-sso": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.730.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/core": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/core": "^3.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/signature-v4": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-stream": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/credential-provider-env": "3.730.0", + "@aws-sdk/credential-provider-http": "3.730.0", + "@aws-sdk/credential-provider-process": "3.730.0", + "@aws-sdk/credential-provider-sso": "3.730.0", + "@aws-sdk/credential-provider-web-identity": "3.730.0", + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.730.0", + "@aws-sdk/credential-provider-http": "3.730.0", + "@aws-sdk/credential-provider-ini": "3.730.0", + "@aws-sdk/credential-provider-process": "3.730.0", + "@aws-sdk/credential-provider-sso": "3.730.0", + "@aws-sdk/credential-provider-web-identity": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.730.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/token-providers": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-logger": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@smithy/core": "^3.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/nested-clients": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.730.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.730.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.730.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/token-providers": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/nested-clients": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/types": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-endpoints": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.730.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.730.0", + "@aws-sdk/types": "3.723.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/abort-controller": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/config-resolver": { + "version": "4.1.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/core": { + "version": "3.5.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.0.8", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.6", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/hash-node": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/invalid-dependency": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-content-length": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-endpoint": { + "version": "4.1.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.5.3", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", + "@smithy/util-middleware": "^4.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-retry": { + "version": "4.1.12", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/service-error-classification": "^4.0.5", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-serde": { + "version": "4.0.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-stack": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/node-config-provider": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/node-http-handler": { + "version": "4.0.6", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/property-provider": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/protocol-http": { + "version": "5.1.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/querystring-builder": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/querystring-parser": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/service-error-classification": { + "version": "4.0.5", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/signature-v4": { + "version": "5.1.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/smithy-client": { + "version": "4.4.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.5.3", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/types": { + "version": "4.3.1", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/url-parser": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.19", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.19", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.1.4", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-endpoints": { + "version": "3.0.6", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-middleware": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-retry": { + "version": "4.0.5", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.0.5", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-stream": { + "version": "4.2.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/lib-storage": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.7", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/smithy-client": "^3.4.3", + "buffer": "5.6.0", + "events": "3.3.0", + "stream-browserify": "3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-s3": "^3.693.0" + } + }, + "node_modules/@aws-sdk/lib-storage/node_modules/buffer": { + "version": "5.6.0", + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-arn-parser": "3.693.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-stream": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.620.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.620.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-api-gateway": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-ec2": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-format-url": "3.693.0", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-arn-parser": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-stream": "^3.3.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-signing": "3.363.0", + "@aws-sdk/types": "3.357.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sts/node_modules/@aws-sdk/types": { + "version": "3.357.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sts/node_modules/@smithy/types": { + "version": "1.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing": { + "version": "3.363.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/protocol-http": "^1.1.0", + "@smithy/signature-v4": "^1.0.1", + "@smithy/types": "^1.1.0", + "@smithy/util-middleware": "^1.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@aws-crypto/util": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@aws-sdk/types": { + "version": "3.357.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/eventstream-codec": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^1.2.0", + "@smithy/util-hex-encoding": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/is-array-buffer": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/property-provider": { + "version": "1.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/protocol-http": { + "version": "1.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/signature-v4": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^1.1.0", + "@smithy/is-array-buffer": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-hex-encoding": "^1.1.0", + "@smithy/util-middleware": "^1.1.0", + "@smithy/util-uri-escape": "^1.1.0", + "@smithy/util-utf8": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/types": { + "version": "1.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/util-buffer-from": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/util-hex-encoding": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/util-middleware": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/util-uri-escape": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing/node_modules/@smithy/util-utf8": { + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-stack": { + "version": "3.342.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.637.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.637.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/core": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.5", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/middleware-logger": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@smithy/core": "^3.1.5", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/types": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/util-endpoints": { + "version": "3.743.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "@smithy/util-endpoints": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.734.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.758.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/hash-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/middleware-retry": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/querystring-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/service-error-classification": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.7", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/config-resolver": "^4.0.1", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-retry": { + "version": "4.0.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-stream": { + "version": "4.1.2", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/property-provider": { + "version": "3.46.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.46.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/property-provider/node_modules/@aws-sdk/types": { + "version": "3.46.0", + "license": "Apache-2.0", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@aws-sdk/protocol-http": { + "version": "3.370.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.370.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/protocol-http/node_modules/@aws-sdk/types": { + "version": "3.370.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/protocol-http/node_modules/@smithy/types": { + "version": "1.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.614.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/signature-v4-multi-region": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-format-url": "3.693.0", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/smithy-client": { + "version": "3.342.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-stack": "3.342.0", + "@aws-sdk/types": "3.342.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/smithy-client/node_modules/@aws-sdk/types": { + "version": "3.342.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.614.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.614.0" + } + }, + "node_modules/@aws-sdk/token-providers/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.692.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.637.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "@smithy/util-endpoints": "^2.0.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/querystring-builder": "^3.0.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.37.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.614.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-user-agent-node/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-toolkits/telemetry": { + "version": "1.0.329", + "resolved": "https://registry.npmjs.org/@aws-toolkits/telemetry/-/telemetry-1.0.329.tgz", + "integrity": "sha512-zMkljZDtIAxuZzPTLL5zIxn+zGmk767sbqGIc2ZYuv0sSU+UoYgB3tqwV5KVV2oDPKs5593nwJC97NVHJqzowQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.6", + "cross-spawn": "^7.0.6", + "fs-extra": "^11.1.0", + "lodash": "^4.17.20", + "prettier": "^3.3.2", + "ts-morph": "^23.0.0", + "yargs": "^17.0.1" + } + }, + "node_modules/@aws/chat-client": { + "version": "0.1.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws/chat-client-ui-types": "^0.1.12", + "@aws/language-server-runtimes-types": "^0.1.10", + "@aws/mynah-ui": "^4.28.0" + } + }, + "node_modules/@aws/chat-client-ui-types": { + "version": "0.1.47", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws/language-server-runtimes-types": "^0.1.41" + } + }, + "node_modules/@aws/language-server-runtimes": { + "version": "0.2.116", + "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes/-/language-server-runtimes-0.2.116.tgz", + "integrity": "sha512-wJoNfbDt/OBEuaseXpeMJTYYndpuoAdPNQkVJdRYAgajzCvWZp/yOdgHu4JNoRo949rLYVRidLTxJo7YVc/LQA==", + "dev": true, + "dependencies": { + "@aws/language-server-runtimes-types": "^0.1.50", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/api-logs": "^0.200.0", + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/exporter-logs-otlp-http": "^0.200.0", + "@opentelemetry/exporter-metrics-otlp-http": "^0.200.0", + "@opentelemetry/resources": "^2.0.1", + "@opentelemetry/sdk-logs": "^0.200.0", + "@opentelemetry/sdk-metrics": "^2.0.1", + "@smithy/node-http-handler": "^4.0.4", + "ajv": "^8.17.1", + "aws-sdk": "^2.1692.0", + "hpagent": "^1.2.0", + "jose": "^5.9.6", + "mac-ca": "^3.1.1", + "os-proxy-config": "^1.1.2", + "rxjs": "^7.8.2", + "vscode-languageserver": "^9.0.1", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-uri": "^3.1.0", + "win-ca": "^3.5.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws/language-server-runtimes-types": { + "version": "0.1.50", + "resolved": "https://registry.npmjs.org/@aws/language-server-runtimes-types/-/language-server-runtimes-types-0.1.50.tgz", + "integrity": "sha512-06JBOKQRJJB/Rg7looY6Xxbab6tIzouZ1QUDdOaFj4zjlbDodeGRXr4W1Oo0N7uz0N24tdoMiNvuky3U5fYmPQ==", + "dev": true, + "dependencies": { + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "^3.17.5" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/@opentelemetry/core": { + "version": "2.0.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/@opentelemetry/resources": { + "version": "2.0.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.1", + "@opentelemetry/resources": "2.0.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/@smithy/abort-controller": { + "version": "4.0.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/@smithy/node-http-handler": { + "version": "4.0.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/querystring-builder": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/@smithy/protocol-http": { + "version": "5.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/@smithy/querystring-builder": { + "version": "4.0.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/@smithy/types": { + "version": "4.2.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/ajv": { + "version": "8.17.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/jose": { + "version": "5.10.0", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@aws/language-server-runtimes/node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/vscode-languageserver": { + "version": "9.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/@aws/language-server-runtimes/node_modules/vscode-uri": { + "version": "3.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@aws/mynah-ui": { + "version": "4.35.4", + "hasInstallScript": true, + "license": "Apache License 2.0", + "dependencies": { + "escape-html": "^1.0.3", + "highlight.js": "^11.11.0", + "just-clone": "^6.2.0", + "marked": "^14.1.0", + "sanitize-html": "^2.12.1", + "unescape-html": "^1.1.0" + }, + "peerDependencies": { + "escape-html": "^1.0.3", + "highlight.js": "^11.11.0", + "just-clone": "^6.2.0", + "marked": "^14.1.0", + "sanitize-html": "^2.12.1", + "unescape-html": "^1.1.0" + } + }, + "node_modules/@aws/mynah-ui/node_modules/marked": { + "version": "14.1.4", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@azu/format-text": { + "version": "1.0.2", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@azu/style-format": { + "version": "1.0.1", + "dev": true, + "license": "WTFPL", + "dependencies": { + "@azu/format-text": "^1.0.1" + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.9.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.9.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.20.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.21.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.8.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@typespec/ts-http-runtime": "^0.2.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.12.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@typespec/ts-http-runtime": "^0.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.10.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^4.2.0", + "@azure/msal-node": "^3.5.0", + "open": "^10.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/identity/node_modules/define-lazy-prop": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@azure/identity/node_modules/is-wsl": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@azure/identity/node_modules/open": { + "version": "10.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@azure/logger": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "4.13.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.7.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "15.7.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "3.6.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.7.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@azure/msal-node/node_modules/uuid": { + "version": "8.3.2", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.6", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@bazel/runfiles": { + "version": "6.3.1", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@bcherny/json-schema-ref-parser": { + "version": "10.0.5-fork", + "dev": true, + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@gerhobbelt/gitignore-parser": { + "version": "0.2.0-9", + "license": "Apache License, Version 2.0", + "engines": { + "node": ">=10" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "license": "ISC" + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "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/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "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/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "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/@istanbuljs/schema": { + "version": "0.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@koa/cors": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@koa/router": { + "version": "13.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "http-errors": "^2.0.0", + "koa-compose": "^4.1.0", + "path-to-regexp": "^6.3.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@koa/router/node_modules/path-to-regexp": { + "version": "6.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.200.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "2.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.200.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/sdk-logs": "0.200.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.200.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-exporter-base": "0.200.0", + "@opentelemetry/otlp-transformer": "0.200.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-metrics": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.200.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/otlp-transformer": "0.200.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.200.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/sdk-logs": "0.200.0", + "@opentelemetry/sdk-metrics": "2.0.0", + "@opentelemetry/sdk-trace-base": "2.0.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.200.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.200.0", + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "2.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "2.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.0.0", + "@opentelemetry/resources": "2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.33.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@playwright/browser-chromium": { + "version": "1.49.1", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.49.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "license": "BSD-3-Clause" + }, + "node_modules/@redhat-developer/locators": { + "version": "1.14.2", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "@redhat-developer/page-objects": ">=1.0.0", + "selenium-webdriver": ">=4.6.1" + } + }, + "node_modules/@redhat-developer/page-objects": { + "version": "1.14.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "clipboardy": "^4.0.0", + "clone-deep": "^4.0.1", + "compare-versions": "^6.1.1", + "fs-extra": "^11.3.0", + "type-fest": "^4.41.0" + }, + "peerDependencies": { + "selenium-webdriver": ">=4.6.1", + "typescript": ">=4.6.2" + } + }, + "node_modules/@redhat-developer/page-objects/node_modules/type-fest": { + "version": "4.41.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/config-creator": { + "version": "10.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.1.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader": { + "version": "10.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.1.1", + "@secretlint/resolver": "^10.1.1", + "@secretlint/types": "^10.1.1", + "ajv": "^8.17.1", + "debug": "^4.4.1", + "rc-config-loader": "^4.1.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader/node_modules/ajv": { + "version": "8.17.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@secretlint/config-loader/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/core": { + "version": "10.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.1.1", + "@secretlint/types": "^10.1.1", + "debug": "^4.4.1", + "structured-source": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter": { + "version": "10.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/resolver": "^10.1.1", + "@secretlint/types": "^10.1.1", + "@textlint/linter-formatter": "^14.8.4", + "@textlint/module-interop": "^14.8.4", + "@textlint/types": "^14.8.4", + "chalk": "^4.1.2", + "debug": "^4.4.1", + "pluralize": "^8.0.0", + "strip-ansi": "^6.0.1", + "table": "^6.9.0", + "terminal-link": "^2.1.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@secretlint/formatter/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@secretlint/node": { + "version": "10.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-loader": "^10.1.1", + "@secretlint/core": "^10.1.1", + "@secretlint/formatter": "^10.1.1", + "@secretlint/profiler": "^10.1.1", + "@secretlint/source-creator": "^10.1.1", + "@secretlint/types": "^10.1.1", + "debug": "^4.4.1", + "p-map": "^7.0.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/profiler": { + "version": "10.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/resolver": { + "version": "10.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/secretlint-formatter-sarif": { + "version": "10.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "node-sarif-builder": "^2.0.3" + } + }, + "node_modules/@secretlint/secretlint-rule-no-dotenv": { + "version": "10.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.1.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/secretlint-rule-preset-recommend": { + "version": "10.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/source-creator": { + "version": "10.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.1.1", + "istextorbinary": "^9.5.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/types": { + "version": "10.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.2.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.0.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^2.0.0" + } + }, + "node_modules/@sinonjs/fake-timers/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "6.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.1", + "dev": true, + "license": "(Unlicense OR Apache-2.0)" + }, + "node_modules/@smithy/abort-controller": { + "version": "3.1.9", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "3.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "3.0.13", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "2.5.7", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-stream": "^3.3.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "3.2.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "3.1.10", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^3.7.2", + "@smithy/util-hex-encoding": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "3.0.14", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.13", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "3.0.13", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.13", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "3.0.13", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^3.1.10", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "3.1.10", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^4.0.0", + "@smithy/chunked-blob-reader-native": "^3.0.1", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-node": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/hash-node/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/hash-node/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/hash-node/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "3.1.10", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/hash-stream-node/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/hash-stream-node/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/hash-stream-node/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/md5-js/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/md5-js/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/md5-js/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "3.0.13", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "3.2.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.7", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "3.1.12", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "3.1.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "4.2.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "3.7.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.7", + "@smithy/middleware-endpoint": "^3.2.8", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-stream": "^3.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "3.7.2", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-base64": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-base64/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-base64/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-base64/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^3.0.13", + "@smithy/credential-provider-imds": "^3.2.8", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "2.1.7", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "3.3.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^4.1.3", + "@smithy/node-http-handler": "^3.3.3", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-stream/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-stream/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-stream/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "3.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin": { + "version": "2.11.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.13.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "8.16.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "8.16.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.16.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "8.16.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/typescript-estree": "8.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.16.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.16.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/espree": { + "version": "10.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/minimatch": { + "version": "9.0.5", + "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/@stylistic/eslint-plugin/node_modules/picomatch": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@svgdotjs/svg.js": { + "version": "3.2.4", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Fuzzyma" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@textlint/ast-node-types": { + "version": "14.8.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter": { + "version": "14.8.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@azu/format-text": "^1.0.2", + "@azu/style-format": "^1.0.1", + "@textlint/module-interop": "14.8.4", + "@textlint/resolver": "14.8.4", + "@textlint/types": "14.8.4", + "chalk": "^4.1.2", + "debug": "^4.4.1", + "js-yaml": "^3.14.1", + "lodash": "^4.17.21", + "pluralize": "^2.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "table": "^6.9.0", + "text-table": "^0.2.0" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/js-yaml": { + "version": "3.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/pluralize": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/module-interop": { + "version": "14.8.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/resolver": { + "version": "14.8.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/types": { + "version": "14.8.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@textlint/ast-node-types": "14.8.4" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@ts-morph/common": { + "version": "0.24.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "^3.3.2", + "minimatch": "^9.0.4", + "mkdirp": "^3.0.1", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "9.0.5", + "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/@ts-morph/common/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/adm-zip": { + "version": "0.4.34", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/async-lock": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/bytes": { + "version": "3.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.2", + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "node_modules/@types/circular-dependency-plugin": { + "version": "5.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "webpack": "^5.1.0" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cross-spawn": { + "version": "6.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/diff": { + "version": "5.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/eslint": { + "version": "8.44.8", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.28", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/glob": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "^5.1.2", + "@types/node": "*" + } + }, + "node_modules/@types/glob/node_modules/@types/minimatch": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/he": { + "version": "1.2.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "license": "MIT" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jaro-winkler": { + "version": "0.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/js-yaml": { + "version": "4.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jsdom": { + "version": "21.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.3", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "3.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.14.182", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lokijs": { + "version": "1.5.14", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "13.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime-types": { + "version": "2.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/minimatch": { + "version": "3.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/minimist": { + "version": "1.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mocha": { + "version": "10.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.8.4", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prettier": { + "version": "2.7.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prismjs": { + "version": "1.26.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/proper-lockfile": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/readline-sync": { + "version": "1.4.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/selenium-webdriver": { + "version": "4.1.28", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/ws": "*" + } + }, + "node_modules/@types/semver": { + "version": "7.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.13.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/sinon": { + "version": "10.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinonjs/fake-timers": "^7.1.0" + } + }, + "node_modules/@types/sinon/node_modules/@sinonjs/fake-timers": { + "version": "7.1.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stream-buffers": { + "version": "3.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/svgdom": { + "version": "0.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tcp-port-used": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "license": "MIT" + }, + "node_modules/@types/vscode": { + "version": "1.83.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/vscode-webview": { + "version": "1.57.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/webpack-env": { + "version": "1.18.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.5.5", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/xml2js": { + "version": "0.4.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/type-utils": "7.14.1", + "@typescript-eslint/utils": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.14.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/typescript-estree": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "7.14.1", + "@typescript-eslint/utils": "7.14.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.14.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.14.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/visitor-keys": "7.14.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "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/@typescript-eslint/utils": { + "version": "7.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.14.1", + "@typescript-eslint/types": "7.14.1", + "@typescript-eslint/typescript-estree": "7.14.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.14.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@typespec/ts-http-runtime/node_modules/agent-base": { + "version": "7.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@typespec/ts-http-runtime/node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@typespec/ts-http-runtime/node_modules/https-proxy-agent": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "dev": true, + "license": "ISC" + }, + "node_modules/@vscode/codicons": { + "version": "0.0.33", + "dev": true, + "license": "CC-BY-4.0" + }, + "node_modules/@vscode/debugprotocol": { + "version": "1.64.0", + "license": "MIT" + }, + "node_modules/@vscode/test-electron": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "jszip": "^3.10.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@vscode/test-web": { + "version": "0.0.65", + "dev": true, + "license": "MIT", + "dependencies": { + "@koa/cors": "^5.0.0", + "@koa/router": "^13.1.0", + "@playwright/browser-chromium": "^1.49.0", + "glob": "^11.0.0", + "gunzip-maybe": "^1.4.2", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "koa": "^2.15.3", + "koa-morgan": "^1.0.1", + "koa-mount": "^4.0.0", + "koa-static": "^5.0.0", + "minimist": "^1.2.8", + "playwright": "^1.49.0", + "tar-fs": "^3.0.6", + "vscode-uri": "^3.0.8" + }, + "bin": { + "vscode-test-web": "out/server/index.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@vscode/test-web/node_modules/agent-base": { + "version": "7.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@vscode/test-web/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@vscode/test-web/node_modules/glob": { + "version": "11.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/test-web/node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@vscode/test-web/node_modules/https-proxy-agent": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@vscode/test-web/node_modules/jackspeak": { + "version": "4.0.2", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/test-web/node_modules/lru-cache": { + "version": "11.0.2", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@vscode/test-web/node_modules/minimatch": { + "version": "10.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/test-web/node_modules/path-scurry": { + "version": "2.0.0", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/test-web/node_modules/tar-fs": { + "version": "3.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" + } + }, + "node_modules/@vscode/test-web/node_modules/tar-stream": { + "version": "3.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/@vscode/test-web/node_modules/vscode-uri": { + "version": "3.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/vsce": { + "version": "2.19.0", + "dev": true, + "license": "MIT", + "dependencies": { + "azure-devops-node-api": "^11.0.1", + "chalk": "^2.4.2", + "cheerio": "^1.0.0-rc.9", + "commander": "^6.1.0", + "glob": "^7.0.6", + "hosted-git-info": "^4.0.2", + "jsonc-parser": "^3.2.0", + "leven": "^3.1.0", + "markdown-it": "^12.3.2", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "semver": "^5.1.0", + "tmp": "^0.2.1", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.5.0", + "yauzl": "^2.3.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 14" + }, + "optionalDependencies": { + "keytar": "^7.7.0" + } + }, + "node_modules/@vscode/vsce-sign": { + "version": "2.0.6", + "dev": true, + "hasInstallScript": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optionalDependencies": { + "@vscode/vsce-sign-alpine-arm64": "2.0.5", + "@vscode/vsce-sign-alpine-x64": "2.0.5", + "@vscode/vsce-sign-darwin-arm64": "2.0.5", + "@vscode/vsce-sign-darwin-x64": "2.0.5", + "@vscode/vsce-sign-linux-arm": "2.0.5", + "@vscode/vsce-sign-linux-arm64": "2.0.5", + "@vscode/vsce-sign-linux-x64": "2.0.5", + "@vscode/vsce-sign-win32-arm64": "2.0.5", + "@vscode/vsce-sign-win32-x64": "2.0.5" + } + }, + "node_modules/@vscode/vsce-sign-darwin-arm64": { + "version": "2.0.5", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@vscode/vsce/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@vscode/vsce/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@vscode/vsce/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/vsce/node_modules/entities": { + "version": "2.1.0", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@vscode/vsce/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@vscode/vsce/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/vsce/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@vscode/vsce/node_modules/linkify-it": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/@vscode/vsce/node_modules/markdown-it": { + "version": "12.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/@vscode/vsce/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@vscode/vsce/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@vscode/vsce/node_modules/xml2js": { + "version": "0.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.21.3", + "@vue/shared": "3.3.4", + "estree-walker": "^2.0.2", + "source-map-js": "^1.0.2" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.3.4", + "@vue/shared": "3.3.4" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.15", + "@vue/compiler-core": "3.3.4", + "@vue/compiler-dom": "3.3.4", + "@vue/compiler-ssr": "3.3.4", + "@vue/reactivity-transform": "3.3.4", + "@vue/shared": "3.3.4", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.0", + "postcss": "^8.1.10", + "source-map-js": "^1.0.2" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.3.4", + "@vue/shared": "3.3.4" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.3.4" + } + }, + "node_modules/@vue/reactivity-transform": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.15", + "@vue/compiler-core": "3.3.4", + "@vue/shared": "3.3.4", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.0" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.3.4", + "@vue/shared": "3.3.4" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@vue/runtime-core": "3.3.4", + "@vue/shared": "3.3.4", + "csstype": "^3.1.1" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.3.4", + "@vue/shared": "3.3.4" + }, + "peerDependencies": { + "vue": "3.3.4" + } + }, + "node_modules/@vue/shared": { + "version": "3.3.4", + "license": "MIT" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.7.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@zip.js/zip.js": { + "version": "2.7.41", + "license": "BSD-3-Clause", + "engines": { + "bun": ">=0.7.0", + "deno": ">=1.0.0", + "node": ">=16.5.0" + } + }, + "node_modules/a-sync-waterfall": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/accepts": { + "version": "1.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.5.10", + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.8.2", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/amazon-q-vscode": { + "resolved": "packages/amazonq", + "link": true + }, + "node_modules/amazon-states-language-service": { + "version": "1.16.1", + "license": "MIT", + "dependencies": { + "js-yaml": "^4.1.0", + "jsonata": "2.0.5", + "lodash": "^4.17.21", + "vscode-json-languageservice": "3.4.9", + "vscode-languageserver": "^9.0.0", + "vscode-languageserver-textdocument": "^1.0.0", + "vscode-languageserver-types": "^3.17.5", + "yaml-language-server": "0.15.0" + } + }, + "node_modules/amazon-states-language-service/node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/amazon-states-language-service/node_modules/vscode-languageserver": { + "version": "9.0.1", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/amazon-states-language-service/node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-gray": { + "version": "0.1.1", + "license": "MIT", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansi-wrap": { + "version": "0.1.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "dev": true, + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "1.1.7", + "dev": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/are-we-there-yet/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "license": "Python-2.0" + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/assert": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" + } + }, + "node_modules/ast-types": { + "version": "0.9.14", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.5", + "license": "MIT" + }, + "node_modules/async-lock": { + "version": "1.4.0", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-core-vscode": { + "resolved": "packages/core", + "link": true + }, + "node_modules/aws-sdk": { + "version": "2.1692.0", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-sdk/node_modules/buffer": { + "version": "4.9.2", + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/aws-sdk/node_modules/events": { + "version": "1.1.1", + "license": "MIT", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/aws-ssm-document-language-service": { + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "vscode-json-languageservice": "3.8.3", + "vscode-languageserver": "^6.1.1", + "yaml": "^1.10.0", + "yaml-language-server": "0.10.1" + } + }, + "node_modules/aws-ssm-document-language-service/node_modules/argparse": { + "version": "1.0.10", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aws-ssm-document-language-service/node_modules/js-yaml": { + "version": "3.14.1", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/aws-ssm-document-language-service/node_modules/jsonc-parser": { + "version": "2.3.1", + "license": "MIT" + }, + "node_modules/aws-ssm-document-language-service/node_modules/prettier": { + "version": "2.0.5", + "license": "MIT", + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/aws-ssm-document-language-service/node_modules/vscode-json-languageservice": { + "version": "3.8.3", + "license": "MIT", + "dependencies": { + "jsonc-parser": "^2.2.1", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.15.1", + "vscode-nls": "^4.1.2", + "vscode-uri": "^2.1.2" + } + }, + "node_modules/aws-ssm-document-language-service/node_modules/vscode-jsonrpc": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8.0.0 || >=10.0.0" + } + }, + "node_modules/aws-ssm-document-language-service/node_modules/vscode-languageserver-protocol": { + "version": "3.14.1", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "^4.0.0", + "vscode-languageserver-types": "3.14.0" + } + }, + "node_modules/aws-ssm-document-language-service/node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { + "version": "3.14.0", + "license": "MIT" + }, + "node_modules/aws-ssm-document-language-service/node_modules/vscode-nls": { + "version": "4.1.2", + "license": "MIT" + }, + "node_modules/aws-ssm-document-language-service/node_modules/yaml-language-server": { + "version": "0.10.1", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "jsonc-parser": "^2.2.1", + "request-light": "^0.2.4", + "vscode-json-languageservice": "^3.6.0", + "vscode-languageserver": "^5.2.1", + "vscode-languageserver-types": "^3.15.1", + "vscode-nls": "^4.1.2", + "vscode-uri": "^2.1.1", + "yaml-ast-parser-custom-tags": "0.0.43" + }, + "bin": { + "yaml-language-server": "bin/yaml-language-server" + }, + "engines": { + "node": "*" + }, + "optionalDependencies": { + "prettier": "2.0.5" + } + }, + "node_modules/aws-ssm-document-language-service/node_modules/yaml-language-server/node_modules/vscode-languageserver": { + "version": "5.2.1", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.14.1", + "vscode-uri": "^1.0.6" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/aws-ssm-document-language-service/node_modules/yaml-language-server/node_modules/vscode-languageserver/node_modules/vscode-uri": { + "version": "1.0.8", + "license": "MIT" + }, + "node_modules/aws-toolkit-vscode": { + "resolved": "packages/toolkit", + "link": true + }, + "node_modules/azure-devops-node-api": { + "version": "11.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "node_modules/b4a": { + "version": "1.6.4", + "dev": true, + "license": "ISC" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.2.2", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bare-fs": { + "version": "2.3.0", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^1.0.0" + } + }, + "node_modules/bare-os": { + "version": "2.3.0", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bare-path": { + "version": "2.1.2", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, + "node_modules/bare-stream": { + "version": "1.0.0", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "streamx": "^2.16.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/batch": { + "version": "0.6.1", + "dev": true, + "license": "MIT" + }, + "node_modules/big.js": { + "version": "5.2.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/binaryextensions": { + "version": "6.11.0", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "dev": true, + "license": "MIT" + }, + "node_modules/bn.js": { + "version": "5.2.1", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.3", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/bonjour-service": { + "version": "1.0.13", + "dev": true, + "license": "MIT", + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/boundary": { + "version": "2.0.0", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/bowser": { + "version": "2.11.0", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/brotli": { + "version": "1.3.3", + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "license": "ISC" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.2", + "license": "ISC", + "dependencies": { + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.4", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.6", + "readable-stream": "^3.6.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/browserify-zlib": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "~0.2.0" + } + }, + "node_modules/browserify-zlib/node_modules/pako": { + "version": "0.2.9", + "dev": true, + "license": "MIT" + }, + "node_modules/browserslist": { + "version": "4.23.1", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001629", + "electron-to-chromium": "^1.4.796", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.16" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/buffer/node_modules/ieee754": { + "version": "1.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/c8": { + "version": "9.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=14.14.0" + } + }, + "node_modules/c8/node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/cache-content-type": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^2.1.18", + "ylru": "^1.2.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.2", + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-callsite/node_modules/callsites": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.2.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys/node_modules/quick-lru": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001638", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "dev": true, + "license": "ISC" + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "4.0.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/circular-dependency-plugin": { + "version": "5.2.2", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "webpack": ">=4.0.1" + } + }, + "node_modules/clean-regexp": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/clean-regexp/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/cli-color": { + "version": "2.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.61", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.15", + "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/clipboardy": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^8.0.1", + "is-wsl": "^3.1.0", + "is64bit": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/execa": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/clipboardy/node_modules/get-stream": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/human-signals": { + "version": "5.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/clipboardy/node_modules/is-stream": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/is-wsl": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/mimic-fn": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/npm-run-path": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/onetime": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/path-key": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/clipboardy/node_modules/strip-final-newline": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-buffer": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/clone-stats": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "node_modules/cloneable-readable/node_modules/readable-stream": { + "version": "2.3.7", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/cloneable-readable/node_modules/safe-buffer": { + "version": "5.1.2", + "license": "MIT" + }, + "node_modules/cloneable-readable/node_modules/string_decoder": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/cockatiel": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.2.1", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.16", + "dev": true, + "license": "MIT" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "license": "MIT", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compare-versions": { + "version": "6.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/compressible": { + "version": "2.0.18", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "license": "MIT" + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/cookies": { + "version": "0.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cookies/node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/core-js-compat": { + "version": "3.37.1", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "5.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cosmiconfig/node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/cosmiconfig/node_modules/import-fresh": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cosmiconfig/node_modules/js-yaml": { + "version": "3.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/cosmiconfig/node_modules/parse-json": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cosmiconfig/node_modules/resolve-from": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "license": "MIT", + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/css-loader": { + "version": "6.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.4", + "postcss-modules-scope": "^3.1.1", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssstyle": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "rrweb-cssom": "^0.6.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/csstype": { + "version": "3.1.2", + "license": "MIT" + }, + "node_modules/cubic2quad": { + "version": "1.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/d": { + "version": "1.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/data-urls": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/decamelize": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/decamelize": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "dev": true, + "license": "MIT" + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-equal": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "5.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/des.js": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.2", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/dfa": { + "version": "1.2.0", + "license": "MIT" + }, + "node_modules/diff": { + "version": "5.2.0", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/dns-packet": { + "version": "5.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "license": "MIT" + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/duplexify": { + "version": "3.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/duplexify/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexify/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/duplexify/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/editions": { + "version": "6.21.0", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "version-range": "^4.13.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.812", + "dev": true, + "license": "ISC" + }, + "node_modules/elliptic": { + "version": "6.6.1", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/enabled": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/envinfo": { + "version": "7.11.1", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "dev": true, + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "license": "MIT" + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "es6-promise": "^4.0.3" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/esbuild": { + "version": "0.15.13", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.15.13", + "@esbuild/linux-loong64": "0.15.13", + "esbuild-android-64": "0.15.13", + "esbuild-android-arm64": "0.15.13", + "esbuild-darwin-64": "0.15.13", + "esbuild-darwin-arm64": "0.15.13", + "esbuild-freebsd-64": "0.15.13", + "esbuild-freebsd-arm64": "0.15.13", + "esbuild-linux-32": "0.15.13", + "esbuild-linux-64": "0.15.13", + "esbuild-linux-arm": "0.15.13", + "esbuild-linux-arm64": "0.15.13", + "esbuild-linux-mips64le": "0.15.13", + "esbuild-linux-ppc64le": "0.15.13", + "esbuild-linux-riscv64": "0.15.13", + "esbuild-linux-s390x": "0.15.13", + "esbuild-netbsd-64": "0.15.13", + "esbuild-openbsd-64": "0.15.13", + "esbuild-sunos-64": "0.15.13", + "esbuild-windows-32": "0.15.13", + "esbuild-windows-64": "0.15.13", + "esbuild-windows-arm64": "0.15.13" + } + }, + "node_modules/esbuild-loader": { + "version": "2.20.0", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.15.6", + "joycon": "^3.0.1", + "json5": "^2.2.0", + "loader-utils": "^2.0.0", + "tapable": "^2.2.0", + "webpack-sources": "^2.2.0" + }, + "funding": { + "url": "https://github.com/privatenumber/esbuild-loader?sponsor=1" + }, + "peerDependencies": { + "webpack": "^4.40.0 || ^5.0.0" + } + }, + "node_modules/esbuild/node_modules/esbuild-darwin-arm64": { + "version": "0.15.13", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-aws-toolkits": { + "resolved": "plugins/eslint-plugin-aws-toolkits", + "link": true + }, + "node_modules/eslint-plugin-header": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=7.7.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-security-node": { + "version": "1.1.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-unicorn": { + "version": "54.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.5", + "@eslint-community/eslint-utils": "^4.4.0", + "@eslint/eslintrc": "^3.0.2", + "ci-info": "^4.0.0", + "clean-regexp": "^1.0.0", + "core-js-compat": "^3.37.0", + "esquery": "^1.5.0", + "indent-string": "^4.0.0", + "is-builtin-module": "^3.2.1", + "jsesc": "^3.0.2", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.27", + "regjsparser": "^0.10.0", + "semver": "^7.6.1", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=18.18" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=8.56.0" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/espree": { + "version": "10.1.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/globals": { + "version": "14.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esniff/node_modules/type": { + "version": "2.7.3", + "dev": true, + "license": "ISC" + }, + "node_modules/espree": { + "version": "9.6.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/event-stream": { + "version": "3.3.5", + "license": "MIT", + "dependencies": { + "duplexer": "^0.1.1", + "from": "^0.1.7", + "map-stream": "0.0.7", + "pause-stream": "^0.0.11", + "split": "^1.0.1", + "stream-combiner": "^0.2.2", + "through": "^2.3.8" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "license": "MIT", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "dev": true, + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/encodeurl": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.12", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/statuses": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ext": { + "version": "1.6.0", + "dev": true, + "license": "ISC", + "dependencies": { + "type": "^2.5.0" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "dev": true, + "license": "ISC" + }, + "node_modules/fancy-log": { + "version": "1.3.3", + "license": "MIT", + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-patch": { + "version": "3.1.1", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.12", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.13.0", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fecha": { + "version": "4.2.1", + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/encodeurl": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/finalhandler/node_modules/statuses": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.2", + "dev": true, + "license": "ISC" + }, + "node_modules/fn.name": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fontkit": { + "version": "2.0.4", + "license": "MIT", + "dependencies": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "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/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from": { + "version": "0.1.7", + "license": "MIT" + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "dev": true, + "license": "Unlicense" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "dev": 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", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "2.7.4", + "dev": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/string-width": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/geometry-interfaces": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stdin": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/glob": { + "version": "10.3.10", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.3", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.5", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/gunzip-maybe": { + "version": "1.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "browserify-zlib": "^0.1.4", + "is-deflate": "^1.0.0", + "is-gzip": "^1.0.0", + "peek-stream": "^1.1.0", + "pumpify": "^1.3.3", + "through2": "^2.0.3" + }, + "bin": { + "gunzip-maybe": "bin.js" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/hash-base": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-sum": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/hash.js": { + "version": "1.1.7", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/highlight.js": { + "version": "11.11.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hosted-git-info": { + "version": "4.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.7", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/hpagent": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/html-entities": { + "version": "2.3.2", + "dev": true, + "license": "MIT" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-assert": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-equal": "~1.0.1", + "http-errors": "~1.8.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-assert/node_modules/http-errors": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "license": "BSD-2-Clause" + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http2": { + "version": "3.3.6", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "9.0.7", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/i18n-ts": { + "version": "1.0.5", + "license": "MIT" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "1.2.1", + "license": "MIT", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/immutable": { + "version": "4.3.0", + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "dev": true, + "license": "ISC" + }, + "node_modules/interpret": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ip-regex": { + "version": "4.3.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is": { + "version": "3.3.0", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "dev": true, + "license": "MIT" + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-deflate": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/is-directory": { + "version": "0.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-electron": { + "version": "2.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-gzip": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-nan": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-promise": { + "version": "2.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-url": { + "version": "1.2.4", + "license": "MIT" + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is2": { + "version": "2.0.7", + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "ip-regex": "^4.1.0", + "is-url": "^1.2.4" + }, + "engines": { + "node": ">=v0.10.0" + } + }, + "node_modules/is64bit": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "system-architecture": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istextorbinary": { + "version": "9.5.0", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "binaryextensions": "^6.11.0", + "editions": "^6.21.0", + "textextensions": "^6.11.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jaro-winkler": { + "version": "0.2.8", + "license": "MIT" + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jmespath": { + "version": "0.16.0", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/jose": { + "version": "5.4.1", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/joycon": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "23.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^3.0.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.7", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.3", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.14.2", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/agent-base": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/http-proxy-agent": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/https-proxy-agent": { + "version": "7.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "license": "MIT" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-to-typescript": { + "version": "13.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcherny/json-schema-ref-parser": "10.0.5-fork", + "@types/json-schema": "^7.0.11", + "@types/lodash": "^4.14.182", + "@types/prettier": "^2.6.1", + "cli-color": "^2.0.2", + "get-stdin": "^8.0.0", + "glob": "^7.1.6", + "glob-promise": "^4.2.2", + "is-glob": "^4.0.3", + "lodash": "^4.17.21", + "minimist": "^1.2.6", + "mkdirp": "^1.0.4", + "mz": "^2.7.0", + "prettier": "^2.6.2" + }, + "bin": { + "json2ts": "dist/src/cli.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/json-schema-to-typescript/node_modules/@types/glob": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/json-schema-to-typescript/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/json-schema-to-typescript/node_modules/glob-promise": { + "version": "4.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "^7.1.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/ahmadnassri" + }, + "peerDependencies": { + "glob": "^7.1.6" + } + }, + "node_modules/json-schema-to-typescript/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/json-schema-to-typescript/node_modules/prettier": { + "version": "2.8.8", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonata": { + "version": "2.0.5", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/just-clone": { + "version": "6.2.0", + "license": "MIT" + }, + "node_modules/just-extend": { + "version": "4.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jwa": { + "version": "1.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keygrip": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tsscmp": "1.0.6" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/keytar": { + "version": "7.9.0", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/koa": { + "version": "2.15.3", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "^1.3.5", + "cache-content-type": "^1.0.0", + "content-disposition": "~0.5.2", + "content-type": "^1.0.4", + "cookies": "~0.9.0", + "debug": "^4.3.2", + "delegates": "^1.0.0", + "depd": "^2.0.0", + "destroy": "^1.0.4", + "encodeurl": "^1.0.2", + "escape-html": "^1.0.3", + "fresh": "~0.5.2", + "http-assert": "^1.3.0", + "http-errors": "^1.6.3", + "is-generator-function": "^1.0.7", + "koa-compose": "^4.1.0", + "koa-convert": "^2.0.0", + "on-finished": "^2.3.0", + "only": "~0.0.2", + "parseurl": "^1.3.2", + "statuses": "^1.5.0", + "type-is": "^1.6.16", + "vary": "^1.1.2" + }, + "engines": { + "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4" + } + }, + "node_modules/koa-compose": { + "version": "4.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/koa-convert": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "co": "^4.6.0", + "koa-compose": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/koa-morgan": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "morgan": "^1.6.1" + } + }, + "node_modules/koa-mount": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.1", + "koa-compose": "^4.1.0" + }, + "engines": { + "node": ">= 7.6.0" + } + }, + "node_modules/koa-send": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "http-errors": "^1.7.3", + "resolve-path": "^1.4.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/koa-send/node_modules/http-errors": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa-static": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.1.0", + "koa-send": "^5.0.0" + }, + "engines": { + "node": ">= 7.6.0" + } + }, + "node_modules/koa-static/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/koa/node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/koa/node_modules/http-errors": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa/node_modules/http-errors/node_modules/depd": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/launch-editor": { + "version": "2.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.7.3" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/linkify-it": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/loader-runner": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/logform": { + "version": "2.4.0", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.5.0", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + } + }, + "node_modules/lokijs": { + "version": "1.5.12", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "license": "Apache-2.0" + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es5-ext": "~0.10.2" + } + }, + "node_modules/mac-ca": { + "version": "3.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "node-forge": "^1.3.1", + "undici": "^6.16.1" + } + }, + "node_modules/mac-system-proxy": { + "version": "1.0.4", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/magic-string": { + "version": "0.30.0", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "dev": true, + "license": "ISC" + }, + "node_modules/map-obj": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/map-stream": { + "version": "0.0.7", + "license": "MIT" + }, + "node_modules/markdown-it": { + "version": "13.0.2", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "3.0.1", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/marked": { + "version": "13.0.1", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/md5": { + "version": "2.3.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.4.7", + "dev": true, + "license": "Unlicense", + "dependencies": { + "fs-monkey": "^1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memoizee": { + "version": "0.4.15", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.53", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + } + }, + "node_modules/meow": { + "version": "9.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/decamelize": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/microbuffer": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/mime": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minimist-options/node_modules/arrify": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/minimist-options/node_modules/is-plain-obj": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha": { + "version": "11.7.1", + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/mocha-junit-reporter": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "md5": "^2.3.0", + "mkdirp": "^3.0.0", + "strip-ansi": "^6.0.1", + "xml": "^1.0.1" + }, + "peerDependencies": { + "mocha": ">=2.2.5" + } + }, + "node_modules/mocha-junit-reporter/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha-junit-reporter/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha-junit-reporter/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha-multi-reporters": { + "version": "1.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "lodash": "^4.17.15" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "mocha": ">=3.1.2" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/chokidar": { + "version": "4.0.3", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/mocha/node_modules/diff": { + "version": "7.0.0", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "10.4.5", + "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/mocha/node_modules/jackspeak": { + "version": "3.4.3", + "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/mocha/node_modules/minimatch": { + "version": "9.0.5", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "4.1.2", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "21.1.1", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/morgan": { + "version": "1.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "dev": true, + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "dev": true, + "license": "ISC" + }, + "node_modules/mvdan-sh": { + "version": "0.10.1", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/mz": { + "version": "2.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/neatequal": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "varstream": "^0.3.2" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/next-tick": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/nise": { + "version": "5.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": ">=5", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/node-abi": { + "version": "3.45.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "dev": true, + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.14", + "dev": true, + "license": "MIT" + }, + "node_modules/node-sarif-builder": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.4", + "fs-extra": "^10.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/node-sarif-builder/node_modules/fs-extra": { + "version": "10.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/noop-logger": { + "version": "0.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "4.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nunjucks": { + "version": "3.2.4", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + }, + "bin": { + "nunjucks-precompile": "bin/precompile" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "chokidar": "^3.3.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/nunjucks/node_modules/commander": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/nwsapi": { + "version": "2.2.7", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/only": { + "version": "0.0.2", + "dev": true + }, + "node_modules/open": { + "version": "8.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "license": "MIT" + }, + "node_modules/os-proxy-config": { + "version": "1.1.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mac-system-proxy": "^1.0.0", + "windows-system-proxy": "^1.0.0" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "^0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "license": "ISC", + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-semver": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.1.0" + } + }, + "node_modules/parse-semver/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/parse5": { + "version": "7.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "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-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "1.9.0", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/path-to-regexp/node_modules/isarray": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "license": [ + "MIT", + "Apache2" + ], + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "license": "MIT", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/peek-stream": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "duplexify": "^3.5.0", + "through2": "^2.0.3" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/playwright": { + "version": "1.49.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.49.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.49.1", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/portfinder": { + "version": "1.0.32", + "license": "MIT", + "dependencies": { + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/async": { + "version": "2.6.4", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.4.33", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.15", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.7", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/prettier-plugin-sh": { + "version": "0.14.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mvdan-sh": "^0.10.1", + "sh-syntax": "^0.4.1" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + }, + "peerDependencies": { + "prettier": "^3.0.3" + } + }, + "node_modules/pretty-quick": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "find-up": "^5.0.0", + "ignore": "^5.3.0", + "mri": "^1.2.0", + "picocolors": "^1.0.0", + "picomatch": "^3.0.1", + "tslib": "^2.6.2" + }, + "bin": { + "pretty-quick": "lib/cli.mjs" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "prettier": "^3.0.0" + } + }, + "node_modules/pretty-quick/node_modules/picomatch": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/private": { + "version": "0.1.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/process": { + "version": "0.11.10", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "license": "MIT" + }, + "node_modules/protobufjs": { + "version": "7.5.1", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/psl": { + "version": "1.9.0", + "dev": true, + "license": "MIT" + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/queue": { + "version": "6.0.2", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.3" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/queue-tick": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc-config-loader": { + "version": "4.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "js-yaml": "^4.1.0", + "json5": "^2.2.2", + "require-from-string": "^2.0.2" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read": { + "version": "1.0.7", + "dev": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "dev": true, + "license": "ISC" + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readline-sync": { + "version": "1.4.10", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/recast": { + "version": "0.11.23", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "0.9.6", + "esprima": "~3.1.0", + "private": "~0.1.5", + "source-map": "~0.5.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/recast/node_modules/ast-types": { + "version": "0.9.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/recast/node_modules/esprima": { + "version": "3.1.3", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/recast/node_modules/source-map": { + "version": "0.5.7", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "dev": true, + "license": "MIT", + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/registry-js": { + "version": "1.16.1", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^3.2.1", + "prebuild-install": "^5.3.5" + } + }, + "node_modules/registry-js/node_modules/decompress-response": { + "version": "4.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/registry-js/node_modules/detect-libc": { + "version": "1.0.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/registry-js/node_modules/mimic-response": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/registry-js/node_modules/node-abi": { + "version": "2.30.1", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.4.1" + } + }, + "node_modules/registry-js/node_modules/node-addon-api": { + "version": "3.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/registry-js/node_modules/prebuild-install": { + "version": "5.3.6", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.7.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/registry-js/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/registry-js/node_modules/simple-get": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/regjsparser": { + "version": "0.10.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "license": "ISC" + }, + "node_modules/replace-ext": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/request-light": { + "version": "0.2.5", + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.3", + "vscode-nls": "^4.1.1" + } + }, + "node_modules/request-light/node_modules/agent-base": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/request-light/node_modules/debug": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/request-light/node_modules/http-proxy-agent": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "agent-base": "4", + "debug": "3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/request-light/node_modules/https-proxy-agent": { + "version": "2.2.4", + "license": "MIT", + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/request-light/node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/request-light/node_modules/vscode-nls": { + "version": "4.1.2", + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.10", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "license": "MIT" + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-path": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "http-errors": "~1.6.2", + "path-is-absolute": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/resolve-path/node_modules/http-errors": { + "version": "1.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/resolve-path/node_modules/inherits": { + "version": "2.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/resolve-path/node_modules/setprototypeof": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/responselike": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + } + }, + "node_modules/restructure": { + "version": "3.0.2", + "license": "MIT" + }, + "node_modules/retry": { + "version": "0.13.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.6.0", + "dev": true, + "license": "MIT" + }, + "node_modules/run-applescript": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.3.1", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/sanitize-filename": { + "version": "1.6.3", + "dev": true, + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sanitize-html": { + "version": "2.13.0", + "license": "MIT", + "dependencies": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^8.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + } + }, + "node_modules/sanitize-html/node_modules/is-plain-object": { + "version": "5.0.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sass": { + "version": "1.69.5", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-loader": { + "version": "16.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.2.1", + "license": "ISC" + }, + "node_modules/saxes": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/secretlint": { + "version": "10.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-creator": "^10.1.1", + "@secretlint/formatter": "^10.1.1", + "@secretlint/node": "^10.1.1", + "@secretlint/profiler": "^10.1.1", + "debug": "^4.4.1", + "globby": "^14.1.0", + "read-pkg": "^8.1.0" + }, + "bin": { + "secretlint": "bin/secretlint.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/secretlint/node_modules/globby": { + "version": "14.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/secretlint/node_modules/hosted-git-info": { + "version": "7.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/secretlint/node_modules/ignore": { + "version": "7.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/secretlint/node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/secretlint/node_modules/lines-and-columns": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/secretlint/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "license": "ISC" + }, + "node_modules/secretlint/node_modules/normalize-package-data": { + "version": "6.0.2", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/secretlint/node_modules/parse-json": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.21.4", + "error-ex": "^1.3.2", + "json-parse-even-better-errors": "^3.0.0", + "lines-and-columns": "^2.0.3", + "type-fest": "^3.8.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/secretlint/node_modules/parse-json/node_modules/type-fest": { + "version": "3.13.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/secretlint/node_modules/path-type": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/secretlint/node_modules/read-pkg": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.1", + "normalize-package-data": "^6.0.0", + "parse-json": "^7.0.0", + "type-fest": "^4.2.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/secretlint/node_modules/slash": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/secretlint/node_modules/type-fest": { + "version": "4.41.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/selenium-webdriver": { + "version": "4.34.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/SeleniumHQ" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/selenium" + } + ], + "license": "Apache-2.0", + "dependencies": { + "@bazel/runfiles": "^6.3.1", + "jszip": "^3.10.1", + "tmp": "^0.2.3", + "ws": "^8.18.2" + }, + "engines": { + "node": ">= 20.0.0" + } + }, + "node_modules/selfsigned": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.6.2", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static/node_modules/encodeurl": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "dev": true, + "license": "ISC" + }, + "node_modules/sh-syntax": { + "version": "0.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/sha.js": { + "version": "2.4.11", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/sinon": { + "version": "14.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^9.1.2", + "@sinonjs/samsam": "^6.1.1", + "diff": "^5.0.0", + "nise": "^5.1.1", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/@sinonjs/fake-timers": { + "version": "9.1.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "dev": true, + "license": "MIT", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.11", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/spdy": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/split": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "license": "BSD-3-Clause" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/stream-buffers": { + "version": "3.0.2", + "license": "Unlicense", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/stream-combiner": { + "version": "0.2.2", + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1", + "through": "~2.3.4" + } + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/streamx": { + "version": "2.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "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": { + "name": "string-width", + "version": "4.2.3", + "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", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "5.2.0", + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "license": "MIT" + }, + "node_modules/structured-source": { + "version": "4.0.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boundary": "^2.0.0" + } + }, + "node_modules/style-loader": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/svg2ttf": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.7.2", + "argparse": "^2.0.1", + "cubic2quad": "^1.2.1", + "lodash": "^4.17.10", + "microbuffer": "^1.0.0", + "svgpath": "^2.1.5" + }, + "bin": { + "svg2ttf": "svg2ttf.js" + } + }, + "node_modules/svgdom": { + "version": "0.1.21", + "license": "MIT", + "dependencies": { + "fontkit": "^2.0.4", + "image-size": "^1.2.1", + "sax": "^1.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Fuzzyma" + } + }, + "node_modules/svgdom/node_modules/sax": { + "version": "1.4.1", + "license": "ISC" + }, + "node_modules/svgicons2svgfont": { + "version": "10.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^7.2.0", + "geometry-interfaces": "^1.1.4", + "glob": "^7.1.6", + "neatequal": "^1.0.0", + "readable-stream": "^3.4.0", + "sax": "^1.2.4", + "svg-pathdata": "^6.0.0" + }, + "bin": { + "svgicons2svgfont": "bin/svgicons2svgfont.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/svgicons2svgfont/node_modules/commander": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/svgicons2svgfont/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/svgicons2svgfont/node_modules/sax": { + "version": "1.2.4", + "dev": true, + "license": "ISC" + }, + "node_modules/svgpath": { + "version": "2.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/synckit": { + "version": "0.8.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/system-architecture": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/table": { + "version": "6.9.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.17.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/targz": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "tar-fs": "^1.8.1" + } + }, + "node_modules/targz/node_modules/bl": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/targz/node_modules/pump": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/targz/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/targz/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/targz/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/targz/node_modules/tar-fs": { + "version": "1.16.5", + "dev": true, + "license": "MIT", + "dependencies": { + "chownr": "^1.0.1", + "mkdirp": "^0.5.1", + "pump": "^1.0.0", + "tar-stream": "^1.1.2" + } + }, + "node_modules/targz/node_modules/tar-stream": { + "version": "1.6.2", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/tcp-port-used": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "debug": "4.3.1", + "is2": "^2.0.6" + } + }, + "node_modules/tcp-port-used/node_modules/debug": { + "version": "4.3.1", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.31.6", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/textextensions": { + "version": "6.11.0", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "license": "MIT" + }, + "node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/time-stamp": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/timers-ext": { + "version": "0.1.7", + "dev": true, + "license": "ISC", + "dependencies": { + "es5-ext": "~0.10.46", + "next-tick": "1" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-buffer": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-buffer/node_modules/isarray": { + "version": "2.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.3", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/triple-beam": { + "version": "1.3.0", + "license": "MIT" + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "dev": true, + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-morph": { + "version": "23.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@ts-morph/common": "~0.24.0", + "code-block-writer": "^13.0.1" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "license": "0BSD" + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.x" + } + }, + "node_modules/ttf2eot": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.6", + "microbuffer": "^1.0.0" + }, + "bin": { + "ttf2eot": "ttf2eot.js" + } + }, + "node_modules/ttf2eot/node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/ttf2woff": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.6", + "microbuffer": "^1.0.0", + "pako": "^1.0.0" + }, + "bin": { + "ttf2woff": "ttf2woff.js" + } + }, + "node_modules/ttf2woff/node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type": { + "version": "1.2.0", + "dev": true, + "license": "ISC" + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.8.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.11", + "dev": true, + "license": "MIT", + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/umd-compat-loader": { + "version": "2.1.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "ast-types": "^0.9.2", + "loader-utils": "^1.0.3", + "recast": "^0.11.17" + } + }, + "node_modules/umd-compat-loader/node_modules/json5": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/umd-compat-loader/node_modules/loader-utils": { + "version": "1.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/underscore": { + "version": "1.13.6", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "6.21.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "license": "MIT" + }, + "node_modules/unescape-html": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unicode-trie/node_modules/pako": { + "version": "0.2.9", + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unzipper": { + "version": "0.12.3", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "~3.7.2", + "duplexer2": "~0.1.4", + "fs-extra": "^11.2.0", + "graceful-fs": "^4.2.2", + "node-int64": "^0.4.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.16", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url": { + "version": "0.10.3", + "license": "MIT", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/url-parse": { + "version": "1.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "license": "MIT" + }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/util": { + "version": "0.12.5", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/varstream": { + "version": "0.3.2", + "dev": true, + "dependencies": { + "readable-stream": "^1.0.33" + }, + "bin": { + "json2varstream": "cli/json2varstream.js", + "varstream2json": "cli/varstream2json.js" + }, + "engines": { + "node": ">=0.10.*" + } + }, + "node_modules/varstream/node_modules/isarray": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/varstream/node_modules/readable-stream": { + "version": "1.1.14", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/varstream/node_modules/string_decoder": { + "version": "0.10.31", + "dev": true, + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/version-range": { + "version": "4.14.0", + "dev": true, + "license": "Artistic-2.0", + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/vinyl": { + "version": "2.2.1", + "license": "MIT", + "dependencies": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vscode-extension-tester": { + "version": "8.16.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@redhat-developer/locators": "^1.14.2", + "@redhat-developer/page-objects": "^1.14.2", + "@types/selenium-webdriver": "^4.1.28", + "@vscode/vsce": "^3.6.0", + "c8": "^10.1.3", + "commander": "^14.0.0", + "compare-versions": "^6.1.1", + "find-up": "7.0.0", + "fs-extra": "^11.3.0", + "glob": "^11.0.3", + "got": "^14.4.7", + "hpagent": "^1.2.0", + "js-yaml": "^4.1.0", + "sanitize-filename": "^1.6.3", + "selenium-webdriver": "^4.34.0", + "targz": "^1.0.1", + "unzipper": "^0.12.3" + }, + "bin": { + "extest": "out/cli.js" + }, + "peerDependencies": { + "mocha": ">=5.2.0", + "typescript": ">=4.6.2" + } + }, + "node_modules/vscode-extension-tester/node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/vscode-extension-tester/node_modules/@sindresorhus/is": { + "version": "7.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/vscode-extension-tester/node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/vscode-extension-tester/node_modules/@vscode/vsce": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/identity": "^4.1.0", + "@secretlint/node": "^10.1.1", + "@secretlint/secretlint-formatter-sarif": "^10.1.1", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.1", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.1", + "@vscode/vsce-sign": "^2.0.0", + "azure-devops-node-api": "^12.5.0", + "chalk": "^4.1.2", + "cheerio": "^1.0.0-rc.9", + "cockatiel": "^3.1.2", + "commander": "^12.1.0", + "form-data": "^4.0.0", + "glob": "^11.0.0", + "hosted-git-info": "^4.0.2", + "jsonc-parser": "^3.2.0", + "leven": "^3.1.0", + "markdown-it": "^14.1.0", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "secretlint": "^10.1.1", + "semver": "^7.5.2", + "tmp": "^0.2.3", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.5.0", + "yauzl": "^2.3.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "keytar": "^7.7.0" + } + }, + "node_modules/vscode-extension-tester/node_modules/@vscode/vsce/node_modules/commander": { + "version": "12.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/vscode-extension-tester/node_modules/azure-devops-node-api": { + "version": "12.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "node_modules/vscode-extension-tester/node_modules/brace-expansion": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/vscode-extension-tester/node_modules/c8": { + "version": "10.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.1", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^7.0.1", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "monocart-coverage-reports": "^2" + }, + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } + } + }, + "node_modules/vscode-extension-tester/node_modules/c8/node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-extension-tester/node_modules/cacheable-lookup": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/vscode-extension-tester/node_modules/cacheable-request": { + "version": "12.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "^4.0.4", + "get-stream": "^9.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.4", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.1", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/vscode-extension-tester/node_modules/commander": { + "version": "14.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/vscode-extension-tester/node_modules/find-up": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-extension-tester/node_modules/find-up/node_modules/locate-path": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-extension-tester/node_modules/find-up/node_modules/path-exists": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/vscode-extension-tester/node_modules/get-stream": { + "version": "9.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-extension-tester/node_modules/glob": { + "version": "11.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vscode-extension-tester/node_modules/glob/node_modules/minimatch": { + "version": "10.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vscode-extension-tester/node_modules/got": { + "version": "14.4.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^7.0.1", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^12.0.1", + "decompress-response": "^6.0.0", + "form-data-encoder": "^4.0.2", + "http2-wrapper": "^2.2.1", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^4.0.1", + "responselike": "^3.0.0", + "type-fest": "^4.26.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/vscode-extension-tester/node_modules/http2-wrapper": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/vscode-extension-tester/node_modules/is-stream": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-extension-tester/node_modules/jackspeak": { + "version": "4.1.1", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vscode-extension-tester/node_modules/linkify-it": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/vscode-extension-tester/node_modules/lowercase-keys": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-extension-tester/node_modules/lru-cache": { + "version": "11.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/vscode-extension-tester/node_modules/markdown-it": { + "version": "14.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/vscode-extension-tester/node_modules/mdurl": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-extension-tester/node_modules/mimic-response": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-extension-tester/node_modules/normalize-url": { + "version": "8.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-extension-tester/node_modules/p-cancelable": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/vscode-extension-tester/node_modules/p-limit": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-extension-tester/node_modules/p-locate": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-extension-tester/node_modules/path-scurry": { + "version": "2.0.0", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vscode-extension-tester/node_modules/responselike": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-extension-tester/node_modules/test-exclude": { + "version": "7.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/vscode-extension-tester/node_modules/test-exclude/node_modules/glob": { + "version": "10.4.5", + "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/vscode-extension-tester/node_modules/test-exclude/node_modules/jackspeak": { + "version": "3.4.3", + "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/vscode-extension-tester/node_modules/test-exclude/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "license": "ISC" + }, + "node_modules/vscode-extension-tester/node_modules/test-exclude/node_modules/minimatch": { + "version": "9.0.5", + "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/vscode-extension-tester/node_modules/test-exclude/node_modules/path-scurry": { + "version": "1.11.1", + "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/vscode-extension-tester/node_modules/type-fest": { + "version": "4.41.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-extension-tester/node_modules/uc.micro": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-extension-tester/node_modules/unicorn-magic": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-extension-tester/node_modules/xml2js": { + "version": "0.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/vscode-extension-tester/node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/vscode-extension-tester/node_modules/yocto-queue": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-json-languageservice": { + "version": "3.4.9", + "license": "MIT", + "dependencies": { + "jsonc-parser": "^2.2.0", + "vscode-languageserver-textdocument": "^1.0.0-next.4", + "vscode-languageserver-types": "^3.15.0-next.6", + "vscode-nls": "^4.1.1", + "vscode-uri": "^2.1.0" + } + }, + "node_modules/vscode-json-languageservice/node_modules/jsonc-parser": { + "version": "2.3.1", + "license": "MIT" + }, + "node_modules/vscode-json-languageservice/node_modules/vscode-nls": { + "version": "4.1.2", + "license": "MIT" + }, + "node_modules/vscode-jsonrpc": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8.0.0 || >=10.0.0" + } + }, + "node_modules/vscode-languageclient": { + "version": "6.1.4", + "license": "MIT", + "dependencies": { + "semver": "^6.3.0", + "vscode-languageserver-protocol": "3.15.3" + }, + "engines": { + "vscode": "^1.41.0" + } + }, + "node_modules/vscode-languageclient/node_modules/semver": { + "version": "6.3.1", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/vscode-languageserver": { + "version": "6.1.1", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "^3.15.3" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.15.3", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "^5.0.1", + "vscode-languageserver-types": "3.15.1" + } + }, + "node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { + "version": "3.15.1", + "license": "MIT" + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "license": "MIT" + }, + "node_modules/vscode-nls": { + "version": "5.2.0", + "license": "MIT" + }, + "node_modules/vscode-nls-dev": { + "version": "4.0.4", + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "clone": "^2.1.2", + "event-stream": "^3.3.4", + "fancy-log": "^1.3.3", + "glob": "^7.2.0", + "iconv-lite": "^0.6.3", + "is": "^3.3.0", + "source-map": "^0.6.1", + "typescript": "^4.5.4", + "vinyl": "^2.2.1", + "xml2js": "^0.5.0", + "yargs": "^17.3.0" + }, + "bin": { + "vscl": "lib/vscl.js" + } + }, + "node_modules/vscode-nls-dev/node_modules/glob": { + "version": "7.2.3", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vscode-nls-dev/node_modules/iconv-lite": { + "version": "0.6.3", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vscode-nls-dev/node_modules/typescript": { + "version": "4.9.5", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/vscode-nls-dev/node_modules/xml2js": { + "version": "0.5.0", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/vscode-uri": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.3.4", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.3.4", + "@vue/compiler-sfc": "3.3.4", + "@vue/runtime-dom": "3.3.4", + "@vue/server-renderer": "3.3.4", + "@vue/shared": "3.3.4" + } + }, + "node_modules/vue-loader": { + "version": "17.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "watchpack": "^2.4.0" + }, + "peerDependencies": { + "webpack": "^4.1.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/vue-style-loader": { + "version": "4.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "node_modules/vue-style-loader/node_modules/hash-sum": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-style-loader/node_modules/json5": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/vue-style-loader/node_modules/loader-utils": { + "version": "1.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/watchpack": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wawoff2": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "woff2_compress.js": "bin/woff2_compress.js", + "woff2_decompress.js": "bin/woff2_decompress.js" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "dev": true, + "license": "MIT", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/web-toolkit": { + "resolved": "packages/core/src/web", + "link": true + }, + "node_modules/web-tree-sitter": { + "version": "0.20.8", + "license": "MIT" + }, + "node_modules/webfont": { + "version": "11.2.26", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^5.2.0", + "deepmerge": "^4.2.2", + "globby": "^11.0.0", + "meow": "^9.0.0", + "nunjucks": "^3.2.3", + "p-limit": "^3.1.0", + "parse-json": "^5.2.0", + "resolve-from": "^5.0.0", + "svg2ttf": "^6.0.2", + "svgicons2svgfont": "^10.0.3", + "ttf2eot": "^2.0.0", + "ttf2woff": "^2.0.2", + "wawoff2": "^2.0.0", + "xml2js": "^0.4.23" + }, + "bin": { + "webfont": "dist/cli.js" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/webfont/node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/webfont/node_modules/xml2js": { + "version": "0.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/webpack": { + "version": "5.95.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "5.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", + "colorette": "^2.0.14", + "commander": "^10.0.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "10.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.11.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.15.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.5", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.4", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.8.2", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/webpack-sources": { + "version": "3.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.0.0", + "license": "MIT", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-pm-runs": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/win-ca": { + "version": "3.5.1", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "is-electron": "^2.2.0", + "make-dir": "^1.3.0", + "node-forge": "^1.2.1", + "split": "^1.0.1" + } + }, + "node_modules/win-ca/node_modules/make-dir": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/windows-system-proxy": { + "version": "1.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "registry-js": "^1.15.1" + } + }, + "node_modules/winston": { + "version": "3.11.0", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.6.0", + "license": "MIT", + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston/node_modules/@colors/colors": { + "version": "1.6.0", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/workerpool": { + "version": "9.3.3", + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "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": { + "name": "wrap-ansi", + "version": "7.0.0", + "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", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.2", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/xtend": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "1.10.2", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yaml-ast-parser-custom-tags": { + "version": "0.0.43", + "license": "Apache-2.0" + }, + "node_modules/yaml-cfn": { + "version": "0.3.2", + "license": "Apache-2.0", + "dependencies": { + "js-yaml": "^4.0.0" + }, + "bin": { + "yaml-cfn": "cli.js" + } + }, + "node_modules/yaml-language-server": { + "version": "0.15.0", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "jsonc-parser": "^2.2.1", + "request-light": "^0.2.4", + "vscode-json-languageservice": "^3.10.0", + "vscode-languageserver": "^5.2.1", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.15.1", + "vscode-nls": "^4.1.2", + "vscode-uri": "^2.1.1", + "yaml-language-server-parser": "0.1.2" + }, + "bin": { + "yaml-language-server": "bin/yaml-language-server" + }, + "engines": { + "node": "*" + }, + "optionalDependencies": { + "prettier": "2.0.5" + } + }, + "node_modules/yaml-language-server-parser": { + "version": "0.1.2", + "license": "Apache-2.0" + }, + "node_modules/yaml-language-server/node_modules/argparse": { + "version": "1.0.10", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/yaml-language-server/node_modules/js-yaml": { + "version": "3.14.1", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/yaml-language-server/node_modules/jsonc-parser": { + "version": "2.3.1", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/prettier": { + "version": "2.0.5", + "license": "MIT", + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/yaml-language-server/node_modules/vscode-json-languageservice": { + "version": "3.11.0", + "license": "MIT", + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "3.16.0-next.2", + "vscode-nls": "^5.0.0", + "vscode-uri": "^2.1.2" + } + }, + "node_modules/yaml-language-server/node_modules/vscode-json-languageservice/node_modules/jsonc-parser": { + "version": "3.2.0", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/vscode-json-languageservice/node_modules/vscode-languageserver-types": { + "version": "3.16.0-next.2", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/vscode-json-languageservice/node_modules/vscode-nls": { + "version": "5.2.0", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/vscode-jsonrpc": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8.0.0 || >=10.0.0" + } + }, + "node_modules/yaml-language-server/node_modules/vscode-languageserver": { + "version": "5.2.1", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.14.1", + "vscode-uri": "^1.0.6" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/yaml-language-server/node_modules/vscode-languageserver-protocol": { + "version": "3.14.1", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "^4.0.0", + "vscode-languageserver-types": "3.14.0" + } + }, + "node_modules/yaml-language-server/node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { + "version": "3.14.0", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/vscode-languageserver/node_modules/vscode-uri": { + "version": "1.0.8", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/vscode-nls": { + "version": "4.1.2", + "license": "MIT" + }, + "node_modules/yargs": { + "version": "17.7.2", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/cliui": { + "version": "8.0.1", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, + "node_modules/ylru": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/amazonq": { + "name": "amazon-q-vscode", + "version": "1.86.0-SNAPSHOT", + "license": "Apache-2.0", + "engines": { + "npm": "^10.1.0", + "vscode": "^1.83.0" + } + }, + "packages/core": { + "name": "aws-core-vscode", + "version": "1.0.0", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@amzn/amazon-q-developer-streaming-client": "file:../../src.gen/@amzn/amazon-q-developer-streaming-client", + "@amzn/codewhisperer-streaming": "file:../../src.gen/@amzn/codewhisperer-streaming", + "@amzn/sagemaker-client": "file:../../src.gen/@amzn/sagemaker-client/1.0.0.tgz", + "@aws-sdk/client-api-gateway": "<3.731.0", + "@aws-sdk/client-apprunner": "<3.731.0", + "@aws-sdk/client-cloudcontrol": "<3.731.0", + "@aws-sdk/client-cloudformation": "<3.731.0", + "@aws-sdk/client-cloudwatch-logs": "<3.731.0", + "@aws-sdk/client-codecatalyst": "<3.731.0", + "@aws-sdk/client-cognito-identity": "<3.731.0", + "@aws-sdk/client-docdb": "<3.731.0", + "@aws-sdk/client-docdb-elastic": "<3.731.0", + "@aws-sdk/client-ec2": "<3.731.0", + "@aws-sdk/client-iam": "<3.731.0", + "@aws-sdk/client-lambda": "<3.731.0", + "@aws-sdk/client-s3": "<3.731.0", + "@aws-sdk/client-sagemaker": "<3.696.0", + "@aws-sdk/client-sfn": "<3.731.0", + "@aws-sdk/client-ssm": "<3.731.0", + "@aws-sdk/client-sso": "<3.731.0", + "@aws-sdk/client-sso-oidc": "<3.731.0", + "@aws-sdk/credential-provider-env": "<3.731.0", + "@aws-sdk/credential-provider-process": "<3.731.0", + "@aws-sdk/credential-provider-sso": "<3.731.0", + "@aws-sdk/credential-providers": "<3.731.0", + "@aws-sdk/lib-storage": "<3.731.0", + "@aws-sdk/property-provider": "<3.731.0", + "@aws-sdk/protocol-http": "<3.731.0", + "@aws-sdk/s3-request-presigner": "<3.731.0", + "@aws-sdk/smithy-client": "<3.731.0", + "@aws-sdk/util-arn-parser": "<3.731.0", + "@aws/mynah-ui": "^4.35.4", + "@gerhobbelt/gitignore-parser": "^0.2.0-9", + "@iarna/toml": "^2.2.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/middleware-retry": "^4.0.3", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/util-retry": "^4.0.1", + "@svgdotjs/svg.js": "^3.0.16", + "@vscode/debugprotocol": "^1.57.0", + "@zip.js/zip.js": "^2.7.41", + "adm-zip": "^0.5.10", + "amazon-states-language-service": "^1.16.1", + "async-lock": "^1.4.0", + "aws-sdk": "^2.1692.0", + "aws-ssm-document-language-service": "^1.0.0", + "bytes": "^3.1.2", + "cross-fetch": "^4.0.0", + "cross-spawn": "^7.0.5", + "diff": "^5.1.0", + "fast-json-patch": "^3.1.1", + "glob": "^10.3.10", + "got": "^11.8.5", + "highlight.js": "^11.9.0", + "http2": "^3.3.6", + "i18n-ts": "^1.0.5", + "immutable": "^4.3.0", + "jaro-winkler": "^0.2.8", + "jose": "5.4.1", + "js-yaml": "^4.1.0", + "jsonc-parser": "^3.2.0", + "lodash": "^4.17.21", + "lokijs": "^1.5.12", + "markdown-it": "^13.0.2", + "mime-types": "^2.1.32", + "node-fetch": "^2.7.0", + "portfinder": "^1.0.32", + "protobufjs": "^7.2.6", + "semver": "^7.5.4", + "stream-buffers": "^3.0.2", + "strip-ansi": "^5.2.0", + "svgdom": "^0.1.0", + "tcp-port-used": "^1.0.1", + "vscode-languageclient": "^6.1.4", + "vscode-languageserver": "^6.1.1", + "vscode-languageserver-protocol": "^3.15.3", + "vscode-languageserver-textdocument": "^1.0.8", + "vue": "^3.3.4", + "web-tree-sitter": "^0.20.8", + "whatwg-url": "^14.0.0", + "winston": "^3.11.0", + "winston-transport": "^4.6.0", + "ws": "^8.16.0", + "xml2js": "^0.6.1", + "yaml-cfn": "^0.3.2" + }, + "devDependencies": { + "@aws-sdk/types": "^3.13.1", + "@aws/chat-client": "^0.1.4", + "@aws/chat-client-ui-types": "^0.1.47", + "@aws/language-server-runtimes": "^0.2.111", + "@aws/language-server-runtimes-types": "^0.1.47", + "@cspotcode/source-map-support": "^0.8.1", + "@sinonjs/fake-timers": "^10.0.2", + "@types/adm-zip": "^0.4.34", + "@types/async-lock": "^1.4.0", + "@types/bytes": "^3.1.0", + "@types/circular-dependency-plugin": "^5.0.8", + "@types/cross-spawn": "^6.0.6", + "@types/diff": "^5.0.7", + "@types/glob": "^8.1.0", + "@types/js-yaml": "^4.0.5", + "@types/jsdom": "^21.1.6", + "@types/lodash": "^4.14.180", + "@types/lokijs": "^1.5.14", + "@types/markdown-it": "^13.0.2", + "@types/mime-types": "^2.1.4", + "@types/mocha": "^10.0.6", + "@types/node": "^16.18.95", + "@types/node-fetch": "^2.6.8", + "@types/prismjs": "^1.26.0", + "@types/proper-lockfile": "^4.1.4", + "@types/readline-sync": "^1.4.8", + "@types/semver": "^7.5.0", + "@types/sinon": "^10.0.5", + "@types/sinonjs__fake-timers": "^8.1.2", + "@types/stream-buffers": "^3.0.7", + "@types/svgdom": "^0.1.2", + "@types/tcp-port-used": "^1.0.1", + "@types/uuid": "^9.0.1", + "@types/whatwg-url": "^11.0.4", + "@types/xml2js": "^0.4.11", + "@vue/compiler-sfc": "^3.3.2", + "c8": "^9.0.0", + "circular-dependency-plugin": "^5.2.2", + "css-loader": "^6.10.0", + "esbuild-loader": "2.20.0", + "file-loader": "^6.2.0", + "jsdom": "^23.0.1", + "json-schema-to-typescript": "^13.1.1", + "marked": "^13.0.1", + "mocha": "^10.1.0", + "mocha-junit-reporter": "^2.2.1", + "mocha-multi-reporters": "^1.5.1", + "readline-sync": "^1.4.9", + "sass": "^1.49.8", + "sass-loader": "^16.0.2", + "sinon": "^14.0.0", + "style-loader": "^3.3.1", + "ts-node": "^10.9.1", + "typescript": "^5.0.4", + "umd-compat-loader": "^2.1.2", + "vue-loader": "^17.2.2", + "vue-style-loader": "^4.1.3", + "webfont": "^11.2.26" + }, + "engines": { + "npm": "^10.1.0", + "vscode": "^1.83.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/client-sts": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-sdk-rds": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb-elastic": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/client-sts": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb-elastic/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb-elastic/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb-elastic/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb-elastic/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb-elastic/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb-elastic/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-docdb/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sts": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-node": "3.693.0", + "@aws-sdk/middleware-host-header": "3.693.0", + "@aws-sdk/middleware-logger": "3.693.0", + "@aws-sdk/middleware-recursion-detection": "3.693.0", + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/region-config-resolver": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@aws-sdk/util-user-agent-browser": "3.693.0", + "@aws-sdk/util-user-agent-node": "3.693.0", + "@smithy/config-resolver": "^3.0.11", + "@smithy/core": "^2.5.2", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/hash-node": "^3.0.9", + "@smithy/invalid-dependency": "^3.0.9", + "@smithy/middleware-content-length": "^3.0.11", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/middleware-retry": "^3.0.26", + "@smithy/middleware-serde": "^3.0.9", + "@smithy/middleware-stack": "^3.0.9", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/url-parser": "^3.0.9", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.26", + "@smithy/util-defaults-mode-node": "^3.0.26", + "@smithy/util-endpoints": "^2.1.5", + "@smithy/util-middleware": "^3.0.9", + "@smithy/util-retry": "^3.0.9", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sts/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/client-sts/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sts/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sts/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sts/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/core": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/core": "^2.5.2", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-middleware": "^3.0.9", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/core/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/fetch-http-handler": "^4.1.0", + "@smithy/node-http-handler": "^3.3.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.6", + "@smithy/smithy-client": "^3.4.3", + "@smithy/types": "^3.7.0", + "@smithy/util-stream": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-ini/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.693.0", + "@aws-sdk/credential-provider-http": "3.693.0", + "@aws-sdk/credential-provider-ini": "3.693.0", + "@aws-sdk/credential-provider-process": "3.693.0", + "@aws-sdk/credential-provider-sso": "3.693.0", + "@aws-sdk/credential-provider-web-identity": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-node/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.693.0", + "@aws-sdk/core": "3.693.0", + "@aws-sdk/token-providers": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-sso/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.693.0" + } + }, + "packages/core/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/middleware-host-header/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/middleware-logger": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/middleware-recursion-detection/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/middleware-sdk-rds": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-format-url": "3.693.0", + "@smithy/middleware-endpoint": "^3.2.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/signature-v4": "^4.2.2", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/middleware-sdk-rds/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@aws-sdk/util-endpoints": "3.693.0", + "@smithy/core": "^2.5.2", + "@smithy/protocol-http": "^4.1.6", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/middleware-user-agent/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/token-providers": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.693.0" + } + }, + "packages/core/node_modules/@aws-sdk/token-providers/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/util-endpoints": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "@smithy/util-endpoints": "^2.1.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.692.0", + "@smithy/types": "^3.7.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "packages/core/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.693.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.693.0", + "@aws-sdk/types": "3.692.0", + "@smithy/node-config-provider": "^3.1.10", + "@smithy/types": "^3.7.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "packages/core/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.0", + "@smithy/querystring-builder": "^4.0.2", + "@smithy/types": "^4.2.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/fetch-http-handler/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/fetch-http-handler/node_modules/@smithy/querystring-builder": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/fetch-http-handler/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/fetch-http-handler/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/fetch-http-handler/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/fetch-http-handler/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry": { + "version": "4.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/service-error-classification": "^4.0.2", + "@smithy/smithy-client": "^4.2.0", + "@smithy/types": "^4.2.0", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-retry": "^4.0.2", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/core": { + "version": "3.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.0.3", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-stream": "^4.2.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/middleware-endpoint": { + "version": "4.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.2.0", + "@smithy/middleware-serde": "^4.0.3", + "@smithy/node-config-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", + "@smithy/url-parser": "^4.0.2", + "@smithy/util-middleware": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/middleware-serde": { + "version": "4.0.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/middleware-stack": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/node-config-provider": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/property-provider": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/smithy-client": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.2.0", + "@smithy/middleware-endpoint": "^4.1.0", + "@smithy/middleware-stack": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "@smithy/util-stream": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/url-parser": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/util-middleware": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/util-stream": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.2", + "@smithy/node-http-handler": "^4.0.4", + "@smithy/types": "^4.2.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/middleware-retry/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/node-http-handler": { + "version": "4.0.4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/querystring-builder": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/node-http-handler/node_modules/@smithy/abort-controller": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/node-http-handler/node_modules/@smithy/querystring-builder": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/node-http-handler/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/protocol-http": { + "version": "5.1.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/protocol-http/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/querystring-parser": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/querystring-parser/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/service-error-classification": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/service-error-classification/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/shared-ini-file-loader/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/util-retry": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/util-retry/node_modules/@smithy/types": { + "version": "4.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/core/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/core/node_modules/@types/node": { + "version": "16.18.95", + "dev": true, + "license": "MIT" + }, + "packages/core/node_modules/brace-expansion": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "packages/core/node_modules/minimatch": { + "version": "5.1.6", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "packages/core/node_modules/mocha": { + "version": "10.8.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "packages/core/node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/core/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "packages/core/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "packages/core/node_modules/workerpool": { + "version": "6.5.1", + "dev": true, + "license": "Apache-2.0" + }, + "packages/core/node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "packages/core/src/web": { + "name": "web-toolkit", + "license": "Apache-2.0", + "dependencies": { + "crypto-browserify": "^3.12.0", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "stream-browserify": "^3.0.0" + }, + "devDependencies": { + "assert": "^2.1.0" + } + }, + "packages/toolkit": { + "name": "aws-toolkit-vscode", + "version": "3.70.0-SNAPSHOT", + "license": "Apache-2.0", + "dependencies": { + "aws-core-vscode": "file:../core/" + }, + "devDependencies": {}, + "engines": { + "npm": "^10.1.0", + "vscode": "^1.83.0" + } + }, + "plugins/eslint-plugin-aws-toolkits": { + "version": "1.0.0", + "license": "Apache-2.0", + "devDependencies": { + "mocha": "^10.1.0" + }, + "engines": { + "npm": "^10.1.0" + } + }, + "plugins/eslint-plugin-aws-toolkits/node_modules/brace-expansion": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "plugins/eslint-plugin-aws-toolkits/node_modules/glob": { + "version": "8.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "plugins/eslint-plugin-aws-toolkits/node_modules/minimatch": { + "version": "5.1.6", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "plugins/eslint-plugin-aws-toolkits/node_modules/mocha": { + "version": "10.8.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "plugins/eslint-plugin-aws-toolkits/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "plugins/eslint-plugin-aws-toolkits/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "plugins/eslint-plugin-aws-toolkits/node_modules/workerpool": { + "version": "6.5.1", + "dev": true, + "license": "Apache-2.0" + }, + "plugins/eslint-plugin-aws-toolkits/node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client": { + "version": "1.0.0", + "hasInstallScript": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.731.0", + "@aws-sdk/credential-provider-node": "3.731.1", + "@aws-sdk/middleware-host-header": "3.731.0", + "@aws-sdk/middleware-logger": "3.731.0", + "@aws-sdk/middleware-recursion-detection": "3.731.0", + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/region-config-resolver": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-user-agent-browser": "3.731.0", + "@aws-sdk/util-user-agent-node": "3.731.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.1", + "@smithy/eventstream-serde-browser": "^4.0.1", + "@smithy/eventstream-serde-config-resolver": "^4.0.1", + "@smithy/eventstream-serde-node": "^4.0.1", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-retry": "^4.0.3", + "@smithy/middleware-serde": "^4.0.1", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.3", + "@smithy/util-defaults-mode-node": "^4.0.3", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "devDependencies": { + "@tsconfig/node18": "18.2.4", + "@types/node": "^18.19.69", + "concurrently": "7.0.0", + "downlevel-dts": "0.10.1", + "rimraf": "^3.0.0", + "typescript": "~5.2.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/client-sso": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.731.0", + "@aws-sdk/middleware-host-header": "3.731.0", + "@aws-sdk/middleware-logger": "3.731.0", + "@aws-sdk/middleware-recursion-detection": "3.731.0", + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/region-config-resolver": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-endpoints": "3.731.0", + "@aws-sdk/util-user-agent-browser": "3.731.0", + "@aws-sdk/util-user-agent-node": "3.731.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/core": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/core": "^3.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/signature-v4": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-stream": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/credential-provider-env": "3.731.0", + "@aws-sdk/credential-provider-http": "3.731.0", + "@aws-sdk/credential-provider-process": "3.731.0", + "@aws-sdk/credential-provider-sso": "3.731.1", + "@aws-sdk/credential-provider-web-identity": "3.731.1", + "@aws-sdk/nested-clients": "3.731.1", + "@aws-sdk/types": "3.731.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.731.0", + "@aws-sdk/credential-provider-http": "3.731.0", + "@aws-sdk/credential-provider-ini": "3.731.1", + "@aws-sdk/credential-provider-process": "3.731.0", + "@aws-sdk/credential-provider-sso": "3.731.1", + "@aws-sdk/credential-provider-web-identity": "3.731.1", + "@aws-sdk/types": "3.731.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.731.0", + "@aws-sdk/core": "3.731.0", + "@aws-sdk/token-providers": "3.731.1", + "@aws-sdk/types": "3.731.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/nested-clients": "3.731.1", + "@aws-sdk/types": "3.731.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/middleware-logger": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-endpoints": "3.731.0", + "@smithy/core": "^3.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/nested-clients": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.731.0", + "@aws-sdk/middleware-host-header": "3.731.0", + "@aws-sdk/middleware-logger": "3.731.0", + "@aws-sdk/middleware-recursion-detection": "3.731.0", + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/region-config-resolver": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-endpoints": "3.731.0", + "@aws-sdk/util-user-agent-browser": "3.731.0", + "@aws-sdk/util-user-agent-node": "3.731.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/token-providers": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/nested-clients": "3.731.1", + "@aws-sdk/types": "3.731.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/types": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/util-endpoints": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/types": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/util-locate-window": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/types": "^4.0.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@babel/runtime": { + "version": "7.26.9", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/eventstream-codec": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/eventstream-serde-browser": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/eventstream-serde-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/eventstream-serde-universal": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/hash-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/middleware-retry": { + "version": "4.0.7", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/querystring-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/service-error-classification": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.7", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.7", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.0.1", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-retry": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-stream": { + "version": "4.1.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@tsconfig/node18": { + "version": "18.2.4", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@types/node": { + "version": "18.19.80", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/@types/uuid": { + "version": "9.0.8", + "license": "MIT" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/bowser": { + "version": "2.11.0", + "license": "MIT" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/concurrently": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + }, + "bin": { + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.0 || >=16.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/date-fns": { + "version": "2.30.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/downlevel-dts": { + "version": "0.10.1", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.2", + "shelljs": "^0.8.3", + "typescript": "next" + }, + "bin": { + "downlevel-dts": "index.js" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/downlevel-dts/node_modules/typescript": { + "version": "5.9.0-dev.20250324", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/escalade": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/fast-xml-parser": { + "version": "4.4.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/function-bind": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/hasown": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/interpret": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/is-core-module": { + "version": "2.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/lodash": { + "version": "4.17.21", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/rechoir": { + "version": "0.6.2", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/regenerator-runtime": { + "version": "0.14.1", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/resolve": { + "version": "1.22.10", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/rxjs": { + "version": "6.6.7", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/semver": { + "version": "7.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/shelljs": { + "version": "0.8.5", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/spawn-command": { + "version": "0.0.2", + "dev": true + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/string-width": { + "version": "4.2.3", + "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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/strnum": { + "version": "1.1.2", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/tree-kill": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/tslib": { + "version": "2.8.1", + "license": "0BSD" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/typescript": { + "version": "5.2.2", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/undici-types": { + "version": "5.26.5", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/wrap-ansi": { + "version": "7.0.0", + "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" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "src.gen/@amzn/amazon-q-developer-streaming-client/node_modules/yargs-parser": { + "version": "20.2.9", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "src.gen/@amzn/codewhisperer-streaming": { + "version": "1.0.0", + "hasInstallScript": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.731.0", + "@aws-sdk/middleware-host-header": "3.731.0", + "@aws-sdk/middleware-logger": "3.731.0", + "@aws-sdk/middleware-recursion-detection": "3.731.0", + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/region-config-resolver": "3.731.0", + "@aws-sdk/token-providers": "3.731.1", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-user-agent-browser": "3.731.0", + "@aws-sdk/util-user-agent-node": "3.731.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.1", + "@smithy/eventstream-serde-browser": "^4.0.1", + "@smithy/eventstream-serde-config-resolver": "^4.0.1", + "@smithy/eventstream-serde-node": "^4.0.1", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-retry": "^4.0.3", + "@smithy/middleware-serde": "^4.0.1", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.3", + "@smithy/util-defaults-mode-node": "^4.0.3", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "devDependencies": { + "@tsconfig/node18": "18.2.4", + "@types/node": "^18.19.69", + "concurrently": "7.0.0", + "downlevel-dts": "0.10.1", + "rimraf": "^3.0.0", + "typescript": "~5.2.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/core": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/core": "^3.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/signature-v4": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/middleware-logger": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-endpoints": "3.731.0", + "@smithy/core": "^3.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/nested-clients": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.731.0", + "@aws-sdk/middleware-host-header": "3.731.0", + "@aws-sdk/middleware-logger": "3.731.0", + "@aws-sdk/middleware-recursion-detection": "3.731.0", + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/region-config-resolver": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-endpoints": "3.731.0", + "@aws-sdk/util-user-agent-browser": "3.731.0", + "@aws-sdk/util-user-agent-node": "3.731.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/token-providers": { + "version": "3.731.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/nested-clients": "3.731.1", + "@aws-sdk/types": "3.731.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/types": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/util-endpoints": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/types": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/util-locate-window": { + "version": "3.723.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.731.0", + "@smithy/types": "^4.0.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.731.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@babel/runtime": { + "version": "7.26.9", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/core": { + "version": "3.1.5", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/credential-provider-imds": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/eventstream-codec": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/eventstream-serde-browser": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/eventstream-serde-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/eventstream-serde-universal": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/hash-node": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/middleware-endpoint": { + "version": "4.0.6", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/middleware-retry": { + "version": "4.0.7", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/node-http-handler": { + "version": "4.0.3", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/property-provider": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/querystring-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/service-error-classification": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/smithy-client": { + "version": "4.1.6", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/types": { + "version": "4.1.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/url-parser": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-base64": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.7", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.7", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.0.1", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-retry": { + "version": "4.0.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-stream": { + "version": "4.1.2", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@tsconfig/node18": { + "version": "18.2.4", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@types/node": { + "version": "18.19.80", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/@types/uuid": { + "version": "9.0.8", + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/bowser": { + "version": "2.11.0", + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/concurrently": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + }, + "bin": { + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.0 || >=16.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/date-fns": { + "version": "2.30.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/downlevel-dts": { + "version": "0.10.1", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.2", + "shelljs": "^0.8.3", + "typescript": "next" + }, + "bin": { + "downlevel-dts": "index.js" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/downlevel-dts/node_modules/typescript": { + "version": "5.9.0-dev.20250324", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/escalade": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/fast-xml-parser": { + "version": "4.4.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/function-bind": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/hasown": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/interpret": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/is-core-module": { + "version": "2.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/lodash": { + "version": "4.17.21", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/rechoir": { + "version": "0.6.2", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/regenerator-runtime": { + "version": "0.14.1", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/resolve": { + "version": "1.22.10", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/rxjs": { + "version": "6.6.7", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/semver": { + "version": "7.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/shelljs": { + "version": "0.8.5", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/spawn-command": { + "version": "0.0.2", + "dev": true + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/string-width": { + "version": "4.2.3", + "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" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/strnum": { + "version": "1.1.2", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/tree-kill": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/tslib": { + "version": "2.8.1", + "license": "0BSD" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/typescript": { + "version": "5.2.2", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/undici-types": { + "version": "5.26.5", + "dev": true, + "license": "MIT" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/wrap-ansi": { + "version": "7.0.0", + "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" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "src.gen/@amzn/codewhisperer-streaming/node_modules/yargs-parser": { + "version": "20.2.9", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + } + } +}