diff --git a/app/aws-lsp-codewhisperer-runtimes/src/agent-standalone.ts b/app/aws-lsp-codewhisperer-runtimes/src/agent-standalone.ts index 49600a91b8..99500ffc83 100644 --- a/app/aws-lsp-codewhisperer-runtimes/src/agent-standalone.ts +++ b/app/aws-lsp-codewhisperer-runtimes/src/agent-standalone.ts @@ -6,6 +6,8 @@ import { CodeWhispererServer, QAgenticChatServerProxy, QConfigurationServerTokenProxy, + TransformConfigurationServerTokenProxy, + AtxNetTransformServerTokenProxy, QLocalProjectContextServerProxy, QNetTransformServerTokenProxy, WorkspaceContextServerTokenProxy, @@ -28,6 +30,8 @@ const props = { CodeWhispererServer, CodeWhispererSecurityScanServerTokenProxy, QConfigurationServerTokenProxy, + TransformConfigurationServerTokenProxy, + AtxNetTransformServerTokenProxy, QNetTransformServerTokenProxy, QAgenticChatServerProxy, IdentityServer.create, diff --git a/app/aws-lsp-codewhisperer-runtimes/src/token-standalone.ts b/app/aws-lsp-codewhisperer-runtimes/src/token-standalone.ts index 266dd06535..87d77d3be1 100644 --- a/app/aws-lsp-codewhisperer-runtimes/src/token-standalone.ts +++ b/app/aws-lsp-codewhisperer-runtimes/src/token-standalone.ts @@ -4,6 +4,8 @@ import { CodeWhispererServerTokenProxy, QChatServerTokenProxy, QConfigurationServerTokenProxy, + TransformConfigurationServerTokenProxy, + AtxNetTransformServerTokenProxy, QNetTransformServerTokenProxy, QLocalProjectContextServerProxy, WorkspaceContextServerTokenProxy, @@ -20,6 +22,8 @@ const props = createTokenRuntimeProps(VERSION, [ CodeWhispererServerTokenProxy, CodeWhispererSecurityScanServerTokenProxy, QConfigurationServerTokenProxy, + TransformConfigurationServerTokenProxy, + AtxNetTransformServerTokenProxy, QNetTransformServerTokenProxy, QChatServerTokenProxy, IdentityServer.create, diff --git a/core/atx-fes-client/amazon-elastic-gumby-frontend-client-1.0.0.tgz b/core/atx-fes-client/amazon-elastic-gumby-frontend-client-1.0.0.tgz new file mode 100644 index 0000000000..1848d6f323 Binary files /dev/null and b/core/atx-fes-client/amazon-elastic-gumby-frontend-client-1.0.0.tgz differ diff --git a/package-lock.json b/package-lock.json index 22b2cb7c8b..9649bd494d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -386,6 +386,902 @@ "yauzl-promise": "^4.0.0" } }, + "node_modules/@amazon/elastic-gumby-frontend-client": { + "version": "1.0.0", + "resolved": "file:core/atx-fes-client/amazon-elastic-gumby-frontend-client-1.0.0.tgz", + "integrity": "sha512-PZ3Zqjalc+SK37maU+/n/XhwmfYp9WT0YIO1PDXbVp2NzP5D4+9h6Et/C1IjF4kc2teGIu7/bV/B1AbHgYq/3w==", + "bundleDependencies": [ + "@aws-sdk/types", + "@aws-sdk/util-user-agent-browser", + "@aws-sdk/util-user-agent-node", + "@smithy/config-resolver", + "@smithy/core", + "@smithy/fetch-http-handler", + "@smithy/hash-node", + "@smithy/invalid-dependency", + "@smithy/middleware-content-length", + "@smithy/middleware-endpoint", + "@smithy/middleware-retry", + "@smithy/middleware-serde", + "@smithy/middleware-stack", + "@smithy/node-config-provider", + "@smithy/node-http-handler", + "@smithy/protocol-http", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/url-parser", + "@smithy/util-base64", + "@smithy/util-body-length-browser", + "@smithy/util-body-length-node", + "@smithy/util-defaults-mode-browser", + "@smithy/util-defaults-mode-node", + "@smithy/util-endpoints", + "@smithy/util-middleware", + "@smithy/util-retry", + "@smithy/util-utf8", + "tslib" + ], + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.658.1", + "@aws-sdk/middleware-host-header": "3.654.0", + "@aws-sdk/middleware-logger": "3.654.0", + "@aws-sdk/middleware-recursion-detection": "3.654.0", + "@aws-sdk/middleware-user-agent": "3.654.0", + "@aws-sdk/region-config-resolver": "3.654.0", + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-user-agent-browser": "3.654.0", + "@aws-sdk/util-user-agent-node": "3.654.0", + "@smithy/config-resolver": "^3.0.8", + "@smithy/core": "^2.4.6", + "@smithy/fetch-http-handler": "^3.2.8", + "@smithy/hash-node": "^3.0.6", + "@smithy/invalid-dependency": "^3.0.6", + "@smithy/middleware-content-length": "^3.0.8", + "@smithy/middleware-retry": "^3.0.21", + "@smithy/middleware-serde": "^3.0.6", + "@smithy/middleware-stack": "^3.0.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/node-http-handler": "^3.2.3", + "@smithy/protocol-http": "^4.1.3", + "@smithy/smithy-client": "^3.3.5", + "@smithy/types": "^3.4.2", + "@smithy/url-parser": "^3.0.6", + "@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.21", + "@smithy/util-defaults-mode-node": "^3.0.21", + "@smithy/util-middleware": "^3.0.6", + "@smithy/util-retry": "^3.0.6", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@aws-sdk/core": { + "version": "3.658.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.658.1.tgz", + "integrity": "sha512-vJVMoMcSKXK2gBRSu9Ywwv6wQ7tXH8VL1fqB1uVxgCqBZ3IHfqNn4zvpMPWrwgO2/3wv7XFyikGQ5ypPTCw4jA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.4.6", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/property-provider": "^3.1.6", + "@smithy/protocol-http": "^4.1.3", + "@smithy/signature-v4": "^4.1.4", + "@smithy/smithy-client": "^3.3.5", + "@smithy/types": "^3.4.2", + "@smithy/util-middleware": "^3.0.6", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.654.0.tgz", + "integrity": "sha512-rxGgVHWKp8U2ubMv+t+vlIk7QYUaRCHaVpmUlJv0Wv6Q0KeO9a42T9FxHphjOTlCGQOLcjCreL9CF8Qhtb4mdQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@aws-sdk/middleware-logger": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.654.0.tgz", + "integrity": "sha512-OQYb+nWlmASyXfRb989pwkJ9EVUMP1CrKn2eyTk3usl20JZmKo2Vjis6I0tLUkMSxMhnBJJlQKyWkRpD/u1FVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.654.0.tgz", + "integrity": "sha512-gKSomgltKVmsT8sC6W7CrADZ4GHwX9epk3GcH6QhebVO3LA9LRbkL3TwOPUXakxxOLLUTYdOZLIOtFf7iH00lg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.654.0.tgz", + "integrity": "sha512-liCcqPAyRsr53cy2tYu4qeH4MMN0eh9g6k56XzI5xd4SghXH5YWh4qOYAlQ8T66ZV4nPMtD8GLtLXGzsH8moFg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@aws-sdk/util-endpoints": "3.654.0", + "@smithy/protocol-http": "^4.1.3", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.654.0.tgz", + "integrity": "sha512-ydGOrXJxj3x0sJhsXyTmvJVLAE0xxuTWFJihTl67RtaO7VRNtd82I3P3bwoMMaDn5WpmV5mPo8fEUDRlBm3fPg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/types": "^3.4.2", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@aws-sdk/types": { + "version": "3.654.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@aws-sdk/util-endpoints": { + "version": "3.654.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.654.0.tgz", + "integrity": "sha512-i902fcBknHs0Irgdpi62+QMvzxE+bczvILXigYrlHL4+PiEnlMVpni5L5W1qCkNZXf8AaMrSBuR1NZAGp6UOUw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", + "@smithy/util-endpoints": "^2.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.654.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/types": "^3.4.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.654.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.654.0", + "@smithy/node-config-provider": "^3.1.7", + "@smithy/types": "^3.4.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/abort-controller": { + "version": "3.1.9", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/config-resolver": { + "version": "3.0.13", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/core": { + "version": "2.5.7", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/core/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/core/node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/core/node_modules/@smithy/util-stream": { + "version": "3.3.4", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/credential-provider-imds": { + "version": "3.2.8", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.9", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/hash-node": { + "version": "3.0.11", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/invalid-dependency": { + "version": "3.0.11", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/middleware-content-length": { + "version": "3.0.13", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/middleware-endpoint": { + "version": "3.2.8", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/middleware-serde": { + "version": "3.0.11", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/middleware-stack": { + "version": "3.0.11", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/node-config-provider": { + "version": "3.1.12", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/property-provider": { + "version": "3.1.11", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/querystring-builder": { + "version": "3.0.11", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/querystring-builder/node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/querystring-parser": { + "version": "3.0.11", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/signature-v4": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.4.tgz", + "integrity": "sha512-5JWeMQYg81TgU4cG+OexAWdvDTs5JDdbEZx+Qr1iPbvo91QFGzjy0IkXAKaXUHqmKUJgSHK0ZxnCkgZpzkeNTA==", + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/smithy-client": { + "version": "3.7.0", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/smithy-client/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/smithy-client/node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/smithy-client/node_modules/@smithy/util-stream": { + "version": "3.3.4", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/types": { + "version": "3.7.2", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/url-parser": { + "version": "3.0.11", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-base64": { + "version": "3.0.0", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-buffer-from/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.34", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.34", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-endpoints": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", + "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-middleware": { + "version": "3.0.11", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "inBundle": true, + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.4.tgz", + "integrity": "sha512-SGhGBG/KupieJvJSZp/rfHHka8BFgj56eek9px4pp7lZbOF+fRiVr4U7A3y3zJD8uGhxq32C5D96HxsTC9BckQ==", + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.3.tgz", + "integrity": "sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==", + "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/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/bowser": { + "version": "2.12.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/tslib": { + "version": "2.8.1", + "inBundle": true, + "license": "0BSD" + }, + "node_modules/@amazon/elastic-gumby-frontend-client/node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "inBundle": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@amzn/amazon-q-developer-streaming-client": { "version": "1.0.0", "resolved": "file:core/q-developer-streaming-client/amzn-amazon-q-developer-streaming-client-1.0.0.tgz", @@ -28198,11 +29094,13 @@ "@amzn/codewhisperer", "@amzn/codewhisperer-runtime", "@amzn/codewhisperer-streaming", - "@amzn/amazon-q-developer-streaming-client" + "@amzn/amazon-q-developer-streaming-client", + "@amazon/elastic-gumby-frontend-client" ], "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { + "@amazon/elastic-gumby-frontend-client": "file:../../core/atx-fes-client/amazon-elastic-gumby-frontend-client-1.0.0.tgz", "@amzn/amazon-q-developer-streaming-client": "file:../../core/q-developer-streaming-client/amzn-amazon-q-developer-streaming-client-1.0.0.tgz", "@amzn/codewhisperer": "file:../../core/codewhisperer/amzn-codewhisperer-1.0.0.tgz", "@amzn/codewhisperer-runtime": "file:../../core/codewhisperer-runtime/amzn-codewhisperer-runtime-1.0.0.tgz", diff --git a/server/aws-lsp-codewhisperer/package.json b/server/aws-lsp-codewhisperer/package.json index f461131308..4fc4b42c76 100644 --- a/server/aws-lsp-codewhisperer/package.json +++ b/server/aws-lsp-codewhisperer/package.json @@ -29,6 +29,7 @@ "postinstall": "node ./script/install_transitive_dep.js" }, "dependencies": { + "@amazon/elastic-gumby-frontend-client": "file:../../core/atx-fes-client/amazon-elastic-gumby-frontend-client-1.0.0.tgz", "@amzn/amazon-q-developer-streaming-client": "file:../../core/q-developer-streaming-client/amzn-amazon-q-developer-streaming-client-1.0.0.tgz", "@amzn/codewhisperer": "file:../../core/codewhisperer/amzn-codewhisperer-1.0.0.tgz", "@amzn/codewhisperer-runtime": "file:../../core/codewhisperer-runtime/amzn-codewhisperer-runtime-1.0.0.tgz", @@ -106,6 +107,7 @@ "@amzn/codewhisperer", "@amzn/codewhisperer-runtime", "@amzn/codewhisperer-streaming", - "@amzn/amazon-q-developer-streaming-client" + "@amzn/amazon-q-developer-streaming-client", + "@amazon/elastic-gumby-frontend-client" ] } diff --git a/server/aws-lsp-codewhisperer/src/index.ts b/server/aws-lsp-codewhisperer/src/index.ts index bc74a8472b..060cf0f747 100644 --- a/server/aws-lsp-codewhisperer/src/index.ts +++ b/server/aws-lsp-codewhisperer/src/index.ts @@ -4,4 +4,5 @@ export * from './language-server/chat/qChatServer' export * from './language-server/agenticChat/qAgenticChatServer' export * from './shared/proxy-server' export * from './language-server/netTransform/netTransformServer' +export * from './language-server/netTransform/atxNetTransformServer' export { AmazonQServiceServerIAM, AmazonQServiceServerToken } from './shared/amazonQServer' diff --git a/server/aws-lsp-codewhisperer/src/language-server/configuration/transformConfigurationServer.ts b/server/aws-lsp-codewhisperer/src/language-server/configuration/transformConfigurationServer.ts new file mode 100644 index 0000000000..138f526f0f --- /dev/null +++ b/server/aws-lsp-codewhisperer/src/language-server/configuration/transformConfigurationServer.ts @@ -0,0 +1,282 @@ +import { + CancellationToken, + CredentialsProvider, + GetConfigurationFromServerParams, + InitializeParams, + Logging, + LSPErrorCodes, + ResponseError, + Server, + BearerCredentials, +} from '@aws/language-server-runtimes/server-interface' +import { AmazonQDeveloperProfile } from '../../shared/amazonQServiceManager/qDeveloperProfiles' +import { ElasticGumbyFrontendClient, ListAvailableProfilesCommand } from '@amazon/elastic-gumby-frontend-client' +import { + DEFAULT_ATX_FES_ENDPOINT_URL, + DEFAULT_ATX_FES_REGION, + ATX_FES_REGION_ENV_VAR, + ATX_FES_ENDPOINT_URL_ENV_VAR, + ATX_FES_ENDPOINTS, +} from '../../shared/constants' +import { getBearerTokenFromProvider } from '../../shared/utils' + +// Transform Configuration Sections +export const TRANSFORM_PROFILES_CONFIGURATION_SECTION = 'aws.transformProfiles' + +/** + * Transform Configuration Server - standalone server for ATX FES profile management + * Completely separate from qConfigurationServer to maintain clean RTS/ATX FES separation + */ +export class TransformConfigurationServer { + private atxClient: ElasticGumbyFrontendClient | null = null + + constructor( + private readonly logging: Logging, + private readonly credentialsProvider: CredentialsProvider + ) { + this.logging.log('TransformConfigurationServer: Constructor called - server created') + } + + /** + * Initialize as standalone LSP server + */ + async initialize(params: InitializeParams): Promise { + this.logging.log('TransformConfigurationServer: Initialize called') + return { + capabilities: {}, + awsServerCapabilities: { + configurationProvider: { + sections: [TRANSFORM_PROFILES_CONFIGURATION_SECTION], + }, + }, + } + } + + /** + * Handle configuration requests for Transform profiles + */ + async getConfiguration(params: GetConfigurationFromServerParams, token: CancellationToken): Promise { + this.logging.log(`TransformConfigurationServer: Configuration requested for section: ${params.section}`) + + switch (params.section) { + case TRANSFORM_PROFILES_CONFIGURATION_SECTION: + const profiles = await this.listAvailableProfiles(token) + return profiles + default: + throw new ResponseError( + LSPErrorCodes.RequestFailed, + `TransformConfigurationServer: Unsupported configuration section: ${params.section}` + ) + } + } + + /** + * Initialize ATX FES client with bearer token authentication + */ + private async initializeAtxClient(): Promise { + try { + if (!this.credentialsProvider?.hasCredentials('bearer')) { + return false + } + + const credentials = (await this.credentialsProvider.getCredentials('bearer')) as BearerCredentials + if (!credentials?.token) { + return false + } + + const region = await this.getClientRegion() + const endpoint = this.getEndpointForRegion(region) + + this.logging.log( + `TransformConfigurationServer: Initializing ATX client with region: ${region}, endpoint: ${endpoint}` + ) + + this.atxClient = new ElasticGumbyFrontendClient({ + region: region, + endpoint: endpoint, + }) + + return true + } catch (error) { + const region = await this.getClientRegion() + const endpoint = this.getEndpointForRegion(region) + this.logging.warn( + `TransformConfigurationServer: Failed to initialize ATX client with region: ${region}, endpoint: ${endpoint}. Error: ${error}` + ) + return false + } + } + + /** + * Get region for ATX FES client - supports dynamic region selection + */ + private async getClientRegion(): Promise { + // Check environment variable first + const envRegion = process.env[ATX_FES_REGION_ENV_VAR] + if (envRegion) { + return envRegion + } + + // Try to get region from profile + const profileRegion = await this.getRegionFromProfile() + if (profileRegion) { + return profileRegion + } + + // Fall back to default + return DEFAULT_ATX_FES_REGION + } + + private async getRegionFromProfile(): Promise { + try { + if (!this.credentialsProvider?.hasCredentials('bearer')) { + return undefined + } + + const tempClient = new ElasticGumbyFrontendClient({ + region: DEFAULT_ATX_FES_REGION, + endpoint: DEFAULT_ATX_FES_ENDPOINT_URL, + }) + + const command = new ListAvailableProfilesCommand({ maxResults: 100 }) + const response = await tempClient.send(command) + const profiles = response.profiles || [] + + const activeProfile = profiles.find((p: any) => p.arn) + if (activeProfile?.arn) { + const arnParts = activeProfile.arn.split(':') + if (arnParts.length >= 4) { + return arnParts[3] + } + } + + return undefined + } catch (error) { + return undefined + } + } + + /** + * Get endpoint URL for the specified region + */ + private getEndpointForRegion(region: string): string { + return ( + process.env[ATX_FES_ENDPOINT_URL_ENV_VAR] || ATX_FES_ENDPOINTS.get(region) || DEFAULT_ATX_FES_ENDPOINT_URL + ) + } + + /** + * Add bearer token authentication to ATX FES command + */ + private async addBearerTokenToCommand(command: any): Promise { + const credentials = (await this.credentialsProvider.getCredentials('bearer')) as BearerCredentials + if (!credentials?.token) { + throw new Error('No bearer token available for ATX FES authentication') + } + + command.middlewareStack?.add( + (next: any) => async (args: any) => { + args.request.headers = { + ...args.request.headers, + Authorization: `Bearer ${credentials.token}`, + } + return next(args) + }, + { step: 'build', priority: 'high' } + ) + } + + /** + * List available Transform profiles using ATX FES ListAvailableProfiles API + * Uses multi-region discovery similar to RTS approach + */ + async listAvailableProfiles(token: CancellationToken): Promise { + try { + const allProfiles: AmazonQDeveloperProfile[] = [] + + for (const [region, endpoint] of ATX_FES_ENDPOINTS) { + try { + if (token?.isCancellationRequested) { + throw new ResponseError(LSPErrorCodes.RequestCancelled, 'Request cancelled') + } + + const profiles = await this.listAvailableProfilesForRegion(region, endpoint) + allProfiles.push(...profiles) + this.logging.log( + `TransformConfigurationServer: Found ${profiles.length} profiles in region ${region}` + ) + } catch (error) { + this.logging.debug(`TransformConfigurationServer: No profiles in region ${region}: ${error}`) + } + } + + this.logging.log( + `TransformConfigurationServer: Total ${allProfiles.length} Transform profiles found across all regions` + ) + return allProfiles + } catch (error) { + this.logging.warn(`TransformConfigurationServer: ListAvailableProfiles failed: ${error}`) + return [] + } + } + + /** + * List available profiles for a specific region (similar to RTS listAvailableCustomizationsForProfileAndRegion) + */ + private async listAvailableProfilesForRegion(region: string, endpoint: string): Promise { + this.logging.log(`TransformConfigurationServer: Querying region: ${region}, endpoint: ${endpoint}`) + + // Create region-specific client (similar to RTS approach) + const regionClient = new ElasticGumbyFrontendClient({ + region: region, + endpoint: endpoint, + }) + + const command = new ListAvailableProfilesCommand({ + maxResults: 100, + }) + + await this.addBearerTokenToCommand(command) + const response = await regionClient.send(command) + + // Convert ATX FES profiles to AmazonQDeveloperProfile format + const transformProfiles: AmazonQDeveloperProfile[] = (response.profiles || []).map((profile: any) => { + const convertedProfile = { + arn: profile.arn || '', + name: profile.profileName || profile.applicationUrl || 'Unnamed Transform Profile', + applicationUrl: (profile.applicationUrl || '').replace(/\/$/, ''), // Strip trailing slash + identityDetails: { + region: region, + accountId: profile.accountId || '', + }, + } + + return convertedProfile + }) + + return transformProfiles + } +} + +/** + * Transform Configuration Server Token - creates standalone Transform configuration server + */ +export const TransformConfigurationServerToken = (): Server => { + return ({ credentialsProvider, lsp, logging }) => { + let transformConfigurationServer: TransformConfigurationServer + + lsp.addInitializer(async params => { + transformConfigurationServer = new TransformConfigurationServer(logging, credentialsProvider) + return transformConfigurationServer.initialize(params) + }) + + lsp.extensions.onGetConfigurationFromServer( + async (params: GetConfigurationFromServerParams, token: CancellationToken) => { + logging.log('TransformConfigurationServer: onGetConfigurationFromServer handler called') + return transformConfigurationServer.getConfiguration(params, token) + } + ) + + return () => {} + } +} diff --git a/server/aws-lsp-codewhisperer/src/language-server/netTransform/atxNetTransformServer.ts b/server/aws-lsp-codewhisperer/src/language-server/netTransform/atxNetTransformServer.ts new file mode 100644 index 0000000000..af7e4039f1 --- /dev/null +++ b/server/aws-lsp-codewhisperer/src/language-server/netTransform/atxNetTransformServer.ts @@ -0,0 +1,93 @@ +import { + CancellationToken, + ExecuteCommandParams, + InitializeParams, + Server, +} from '@aws/language-server-runtimes/server-interface' +import { AtxTokenServiceManager } from '../../shared/amazonQServiceManager/AtxTokenServiceManager' +import { ATXTransformHandler } from './atxTransformHandler' + +// ATX FES Commands +const AtxListAvailableProfilesCommand = 'aws/atxNetTransform/listAvailableProfiles' + +// TODO: Phase 2 - Add remaining ATX FES APIs +// const AtxVerifySessionCommand = 'aws/atxNetTransform/verifySession' // LSP-only implementation +// const AtxListWorkspacesCommand = 'aws/atxNetTransform/listWorkspaces' +// const AtxCreateWorkspaceCommand = 'aws/atxNetTransform/createWorkspace' +// const AtxCreateJobCommand = 'aws/atxNetTransform/createJob' +// const AtxStartJobCommand = 'aws/atxNetTransform/startJob' +// const AtxGetJobCommand = 'aws/atxNetTransform/getJob' +// const AtxStopJobCommand = 'aws/atxNetTransform/stopJob' +// const AtxCreateUploadArtifactURLCommand = 'aws/atxNetTransform/createUploadArtifactURL' +// const AtxCompleteUploadArtifactURLCommand = 'aws/atxNetTransform/completeUploadArtifactURL' +// const AtxCreateDownloadArtifactURLCommand = 'aws/atxNetTransform/createDownloadArtifactURL' +// const AtxListArtifactsCommand = 'aws/atxNetTransform/listArtifacts' +// const AtxListJobStepPlansCommand = 'aws/atxNetTransform/listJobStepPlans' + +// TODO: Phase 2 - Add remaining ATX FES APIs +export const AtxNetTransformServerToken = + (): Server => + ({ workspace, logging, lsp, telemetry, runtime }) => { + let atxTokenServiceManager: AtxTokenServiceManager + let atxTransformHandler: ATXTransformHandler + + const runAtxTransformCommand = async (params: ExecuteCommandParams, _token: CancellationToken) => { + try { + switch (params.command) { + case AtxListAvailableProfilesCommand: { + const maxResults = (params as any).maxResults || 100 + const response = await atxTransformHandler.listAvailableProfiles(maxResults) + return response + } + default: { + throw new Error(`Unknown ATX FES command: ${params.command}`) + } + } + } catch (e: any) { + throw e + } + } + + const onExecuteCommandHandler = async ( + params: ExecuteCommandParams, + _token: CancellationToken + ): Promise => { + return runAtxTransformCommand(params, _token) + } + + const onInitializeHandler = async (params: InitializeParams) => { + return { + capabilities: { + executeCommandProvider: { + commands: [ + AtxListAvailableProfilesCommand, + // TODO: Phase 2: Add remaining ATX FES APIs + // AtxVerifySessionCommand, // LSP-only implementation + // AtxListWorkspacesCommand, + // AtxCreateWorkspaceCommand, + // AtxCreateJobCommand, + // AtxStartJobCommand, + // AtxGetJobCommand, + // AtxStopJobCommand, + // AtxCreateUploadArtifactURLCommand, + // AtxCompleteUploadArtifactURLCommand, + // AtxCreateDownloadArtifactURLCommand, + // AtxListArtifactsCommand, + // AtxListJobStepPlansCommand, + ], + }, + }, + } + } + + const onInitializedHandler = () => { + atxTokenServiceManager = AtxTokenServiceManager.getInstance() + atxTransformHandler = new ATXTransformHandler(atxTokenServiceManager, workspace, logging, runtime) + } + + lsp.addInitializer(onInitializeHandler) + lsp.onInitialized(onInitializedHandler) + lsp.onExecuteCommand(onExecuteCommandHandler) + + return () => {} + } diff --git a/server/aws-lsp-codewhisperer/src/language-server/netTransform/atxTransformHandler.ts b/server/aws-lsp-codewhisperer/src/language-server/netTransform/atxTransformHandler.ts new file mode 100644 index 0000000000..8bd52106ab --- /dev/null +++ b/server/aws-lsp-codewhisperer/src/language-server/netTransform/atxTransformHandler.ts @@ -0,0 +1,185 @@ +import { Logging, Runtime, Workspace } from '@aws/language-server-runtimes/server-interface' +import { ElasticGumbyFrontendClient, ListAvailableProfilesCommand } from '@amazon/elastic-gumby-frontend-client' +import { AtxTokenServiceManager } from '../../shared/amazonQServiceManager/AtxTokenServiceManager' +import { DEFAULT_ATX_FES_ENDPOINT_URL, DEFAULT_ATX_FES_REGION, ATX_FES_REGION_ENV_VAR } from '../../shared/constants' + +/** + * ATX Transform Handler - Business logic for ATX FES Transform operations + * Parallel to RTS TransformHandler but uses AtxTokenServiceManager and ATX FES APIs + */ +export class ATXTransformHandler { + private serviceManager: AtxTokenServiceManager + private workspace: Workspace + private logging: Logging + private runtime: Runtime + private atxClient: ElasticGumbyFrontendClient | null = null + private cachedApplicationUrl: string | null = null + + constructor(serviceManager: AtxTokenServiceManager, workspace: Workspace, logging: Logging, runtime: Runtime) { + this.serviceManager = serviceManager + this.workspace = workspace + this.logging = logging + this.runtime = runtime + + this.serviceManager.registerCacheCallback(() => this.clearApplicationUrlCache()) + } + + /** + * Initialize ATX FES client + */ + private async initializeAtxClient(): Promise { + try { + let region = process.env[ATX_FES_REGION_ENV_VAR] + + if (!region) { + region = await this.getRegionFromProfile() + } + + if (!region) { + region = DEFAULT_ATX_FES_REGION + } + + const endpoint = process.env.TCP_ENDPOINT || DEFAULT_ATX_FES_ENDPOINT_URL + + this.clearApplicationUrlCache() + + this.atxClient = new ElasticGumbyFrontendClient({ + region: region, + endpoint: endpoint, + }) + + return true + } catch (error) { + const region = process.env[ATX_FES_REGION_ENV_VAR] || DEFAULT_ATX_FES_REGION + const endpoint = process.env.TCP_ENDPOINT || DEFAULT_ATX_FES_ENDPOINT_URL + this.logging.log( + `ATX FES Client: Failed to initialize with region: ${region}, endpoint: ${endpoint}. Error: ${error}` + ) + return false + } + } + + private async getRegionFromProfile(): Promise { + try { + if (!this.serviceManager.hasValidCredentials()) { + return undefined + } + + const tempClient = new ElasticGumbyFrontendClient({ + region: DEFAULT_ATX_FES_REGION, + endpoint: DEFAULT_ATX_FES_ENDPOINT_URL, + }) + + const command = new ListAvailableProfilesCommand({ maxResults: 100 }) + const response = await tempClient.send(command) + const profiles = response.profiles || [] + + const activeProfile = profiles.find((p: any) => p.arn) + if (activeProfile?.arn) { + const arnParts = activeProfile.arn.split(':') + if (arnParts.length >= 4) { + return arnParts[3] + } + } + + return undefined + } catch (error) { + return undefined + } + } + + /** + * Add bearer token and Origin header to ATX FES commands + */ + private async addAuthToCommand(command: any): Promise { + if (!this.serviceManager.isReady()) { + throw new Error('Please select a valid Transform profile to continue') + } + + const bearerToken = await this.serviceManager.getBearerToken() + const applicationUrl = await this.getActiveTransformProfileApplicationUrl() + + command.middlewareStack?.add( + (next: any) => async (args: any) => { + if (!args.request.headers) { + args.request.headers = {} + } + args.request.headers['Authorization'] = `Bearer ${bearerToken}` + + if (applicationUrl) { + const cleanOrigin = applicationUrl.endsWith('/') ? applicationUrl.slice(0, -1) : applicationUrl + args.request.headers['Origin'] = cleanOrigin + } + + args.request.headers['Content-Type'] = 'application/json; charset=UTF-8' + args.request.headers['Content-Encoding'] = 'amz-1.0' + + return next(args) + }, + { + step: 'build', + name: 'addAtxAuthMiddleware', + priority: 'high', + } + ) + } + + /** + * List available Transform profiles from ATX FES + */ + async listAvailableProfiles(maxResults: number = 100): Promise<{ profiles: any[] }> { + if (!this.atxClient && !(await this.initializeAtxClient())) { + throw new Error('ATX FES client not initialized') + } + + const command = new ListAvailableProfilesCommand({ + maxResults: maxResults, + }) + + await this.addAuthToCommand(command) + const response = await this.atxClient!.send(command) + + return { profiles: response.profiles || [] } + } + + /** + * Gets the applicationUrl for the active Transform profile with caching + */ + async getActiveTransformProfileApplicationUrl(): Promise { + try { + // Return cached URL if available (avoids expensive profile discovery) + if (this.cachedApplicationUrl) { + return this.cachedApplicationUrl + } + + const response = await this.listAvailableProfiles(100) + const profiles = response.profiles || [] + + // For now, use the first available profile with applicationUrl + // TODO: In future, get active profile ARN from service manager and match exactly + const profileWithUrl = profiles.find((p: any) => p.applicationUrl) + + if (profileWithUrl && profileWithUrl.applicationUrl) { + this.cachedApplicationUrl = profileWithUrl.applicationUrl + return profileWithUrl.applicationUrl + } else { + this.logging.error('ATX FES: No Transform profile found with applicationUrl') + return null + } + } catch (error) { + this.logging.error( + `ATX FES: Error getting applicationUrl: ${error instanceof Error ? error.message : 'Unknown error'}` + ) + return null + } + } + + /** + * Clear cached applicationUrl (for token refresh scenarios) + */ + clearApplicationUrlCache(): void { + this.cachedApplicationUrl = null + } + + // TODO: Phase 2 - Implement remaining ATX FES APIs +} diff --git a/server/aws-lsp-codewhisperer/src/shared/amazonQServer.ts b/server/aws-lsp-codewhisperer/src/shared/amazonQServer.ts index ce00760319..6b0e755378 100644 --- a/server/aws-lsp-codewhisperer/src/shared/amazonQServer.ts +++ b/server/aws-lsp-codewhisperer/src/shared/amazonQServer.ts @@ -8,6 +8,7 @@ import { import { AmazonQBaseServiceManager, QServiceManagerFeatures } from './amazonQServiceManager/BaseAmazonQServiceManager' import { initBaseIAMServiceManager } from './amazonQServiceManager/AmazonQIAMServiceManager' import { initBaseTokenServiceManager } from './amazonQServiceManager/AmazonQTokenServiceManager' +import { AtxTokenServiceManager } from './amazonQServiceManager/AtxTokenServiceManager' const LOGGING_PREFIX = '[AMAZON Q SERVER]: ' @@ -21,10 +22,10 @@ export const AmazonQServiceServerFactory = } /* - The service manager relies on client params to fully initialize, so the initialization needs - to be deferred to the LSP handshake. Dependent servers may assume the service manager is - available when the initialized notification has been received. - */ + The service manager relies on client params to fully initialize, so the initialization needs + to be deferred to the LSP handshake. Dependent servers may assume the service manager is + available when the initialized notification has been received. + */ lsp.addInitializer((_params: InitializeParams) => { amazonQServiceManager = serviceManager({ credentialsProvider, @@ -35,6 +36,16 @@ export const AmazonQServiceServerFactory = sdkInitializator, }) + // Initialize ATX Token Service Manager for ATX FES support + AtxTokenServiceManager.initInstance({ + credentialsProvider, + lsp, + workspace, + logging, + runtime, + sdkInitializator, + }) + return { capabilities: {}, awsServerCapabilities: {}, diff --git a/server/aws-lsp-codewhisperer/src/shared/amazonQServiceManager/AtxTokenServiceManager.ts b/server/aws-lsp-codewhisperer/src/shared/amazonQServiceManager/AtxTokenServiceManager.ts new file mode 100644 index 0000000000..c3416542ee --- /dev/null +++ b/server/aws-lsp-codewhisperer/src/shared/amazonQServiceManager/AtxTokenServiceManager.ts @@ -0,0 +1,79 @@ +import { + CredentialsType, + UpdateConfigurationParams, + CancellationToken, +} from '@aws/language-server-runtimes/server-interface' +import { QServiceManagerFeatures } from './BaseAmazonQServiceManager' +import { TRANSFORM_PROFILES_CONFIGURATION_SECTION } from '../../language-server/configuration/transformConfigurationServer' + +export class AtxTokenServiceManager { + private static instance: AtxTokenServiceManager | null = null + private features: QServiceManagerFeatures + private cacheCallbacks: (() => void)[] = [] + + private constructor(features: QServiceManagerFeatures) { + this.features = features + } + + public static initInstance(features: QServiceManagerFeatures): AtxTokenServiceManager { + if (!AtxTokenServiceManager.instance) { + AtxTokenServiceManager.instance = new AtxTokenServiceManager(features) + return AtxTokenServiceManager.instance + } + throw new Error('ATX Token Service Manager already initialized') + } + + public static getInstance(): AtxTokenServiceManager { + if (!AtxTokenServiceManager.instance) { + throw new Error('ATX Token Service Manager not initialized') + } + return AtxTokenServiceManager.instance + } + + public handleOnCredentialsDeleted(type: CredentialsType): void { + this.clearAllCaches() + } + + public handleOnUpdateConfiguration(params: UpdateConfigurationParams, _token: CancellationToken): void { + if (params.section === TRANSFORM_PROFILES_CONFIGURATION_SECTION) { + this.clearAllCaches() + } + } + + public registerCacheCallback(callback: () => void): void { + this.cacheCallbacks.push(callback) + } + + private clearAllCaches(): void { + this.cacheCallbacks.forEach(callback => callback()) + } + + public hasValidCredentials(): boolean { + return this.features.credentialsProvider.hasCredentials('bearer') + } + + public async getBearerToken(): Promise { + if (!this.hasValidCredentials()) { + throw new Error('No bearer credentials available for ATX') + } + + const credentials = await this.features.credentialsProvider.getCredentials('bearer') + if (!credentials || !('token' in credentials) || !credentials.token) { + throw new Error('Bearer token is null or empty') + } + + return credentials.token + } + + public isReady(): boolean { + return this.hasValidCredentials() + } + + private log(message: string): void { + this.features.logging?.log(`ATX Token Service Manager: ${message}`) + } + + public static resetInstance(): void { + AtxTokenServiceManager.instance = null + } +} diff --git a/server/aws-lsp-codewhisperer/src/shared/amazonQServiceManager/atxTransformProfiles.ts b/server/aws-lsp-codewhisperer/src/shared/amazonQServiceManager/atxTransformProfiles.ts new file mode 100644 index 0000000000..93364ab77f --- /dev/null +++ b/server/aws-lsp-codewhisperer/src/shared/amazonQServiceManager/atxTransformProfiles.ts @@ -0,0 +1,150 @@ +import { CancellationToken, Logging, SsoConnectionType } from '@aws/language-server-runtimes/server-interface' +import { AtxTokenServiceManager } from './AtxTokenServiceManager' +import { ATX_FES_ENDPOINTS } from '../constants' + +export interface AtxTransformProfile { + arn: string | undefined + name: string | undefined + applicationUrl: string | undefined + identityDetails?: AtxIdentityDetails +} + +interface AtxIdentityDetails { + region: string +} + +export interface ListAllAvailableAtxProfilesHandlerParams { + connectionType: SsoConnectionType + logging: Logging + atxEndpoints?: Map + token: CancellationToken +} + +export type ListAllAvailableAtxProfilesHandler = ( + params: ListAllAvailableAtxProfilesHandlerParams +) => Promise + +export const MAX_ATX_PROFILE_PAGES = 10 +const MAX_ATX_PROFILES_PER_PAGE = 10 + +export const getListAllAvailableAtxProfilesHandler = + (atxTokenServiceManager: AtxTokenServiceManager): ListAllAvailableAtxProfilesHandler => + async ({ connectionType, logging, atxEndpoints, token }): Promise => { + if (!connectionType || connectionType !== 'identityCenter') { + logging.debug('ATX Profiles: Connection type is not identityCenter - returning empty response.') + return [] + } + + let allAtxProfiles: AtxTransformProfile[] = [] + const endpoints = atxEndpoints ?? ATX_FES_ENDPOINTS + + if (token.isCancellationRequested) { + return [] + } + + const result = await Promise.allSettled( + Array.from(endpoints.entries(), ([region, endpoint]) => { + return fetchAtxProfilesFromRegion(atxTokenServiceManager, region, endpoint, logging, token) + }) + ) + + if (token.isCancellationRequested) { + return [] + } + + result.forEach((settledResult, index) => { + if (settledResult.status === 'rejected') { + const [region] = Array.from(endpoints.entries())[index] + logging.error( + `ATX Profiles: Failed to fetch from region: ${region}, error: ${settledResult.reason?.name || 'unknown'}` + ) + } + }) + + const fulfilledResults = result.filter(settledResult => settledResult.status === 'fulfilled') + + if (fulfilledResults.length === 0) { + throw new Error('Failed to retrieve ATX profiles from all queried regions') + } + + fulfilledResults.forEach(fulfilledResult => allAtxProfiles.push(...fulfilledResult.value)) + + return allAtxProfiles + } + +async function fetchAtxProfilesFromRegion( + atxTokenServiceManager: AtxTokenServiceManager, + region: string, + endpoint: string, + logging: Logging, + token: CancellationToken +): Promise { + let allRegionalProfiles: AtxTransformProfile[] = [] + let nextToken: string | undefined = undefined + let numberOfPages = 0 + + try { + const { ElasticGumbyFrontendClient, ListAvailableProfilesCommand } = await import( + '@amazon/elastic-gumby-frontend-client' + ) + + const atxClient = new ElasticGumbyFrontendClient({ + region: region, + endpoint: endpoint, + }) + + do { + if (token.isCancellationRequested) { + return allRegionalProfiles + } + + const command: any = new ListAvailableProfilesCommand({ + maxResults: MAX_ATX_PROFILES_PER_PAGE, + nextToken: nextToken, + }) + + const bearerToken = await atxTokenServiceManager.getBearerToken() + command.middlewareStack?.add( + (next: any) => async (args: any) => { + if (!args.request.headers) { + args.request.headers = {} + } + args.request.headers['Authorization'] = `Bearer ${bearerToken}` + return next(args) + }, + { + step: 'build', + name: 'addAtxBearerToken', + priority: 'high', + } + ) + + const response: any = await atxClient.send(command) + + const profiles = + response.profiles?.map((profile: any) => { + const applicationUrl = profile.applicationUrl?.replace(/\/$/, '') || profile.applicationUrl + + return { + arn: profile.arn, + name: profile.profileName || profile.name, + applicationUrl: applicationUrl, + identityDetails: { + region, + }, + } + }) ?? [] + + allRegionalProfiles.push(...profiles) + + nextToken = response.nextToken + numberOfPages++ + } while (nextToken !== undefined && numberOfPages < MAX_ATX_PROFILE_PAGES) + + return allRegionalProfiles + } catch (error) { + logging.error(`ATX Profiles: Error fetching from region: ${region}`) + logging.error(`ATX Profiles: Error details: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`) + throw error + } +} diff --git a/server/aws-lsp-codewhisperer/src/shared/constants.ts b/server/aws-lsp-codewhisperer/src/shared/constants.ts index 33f61a079f..abb5f2cca1 100644 --- a/server/aws-lsp-codewhisperer/src/shared/constants.ts +++ b/server/aws-lsp-codewhisperer/src/shared/constants.ts @@ -12,9 +12,21 @@ export const AWS_Q_ENDPOINTS = new Map([ ['eu-central-1', 'https://q.eu-central-1.amazonaws.com/'], ]) +export const DEFAULT_ATX_FES_REGION = 'us-east-1' + +export const ATX_FES_ENDPOINTS = new Map([ + [DEFAULT_ATX_FES_REGION, `https://api.transform-prod.${DEFAULT_ATX_FES_REGION}.on.aws/`], + ['us-west-2', 'https://api.transform-prod.us-west-2.on.aws/'], +]) + +export const DEFAULT_ATX_FES_ENDPOINT_URL = `https://api.transform-prod.${DEFAULT_ATX_FES_REGION}.on.aws/` + export const AWS_Q_REGION_ENV_VAR = 'AWS_Q_REGION' export const AWS_Q_ENDPOINT_URL_ENV_VAR = 'AWS_Q_ENDPOINT_URL' +export const ATX_FES_REGION_ENV_VAR = 'ATX_FES_REGION' +export const ATX_FES_ENDPOINT_URL_ENV_VAR = 'ATX_FES_ENDPOINT_URL' + export const IDE = 'IDE' export const Q_CONFIGURATION_SECTION = 'aws.q' diff --git a/server/aws-lsp-codewhisperer/src/shared/proxy-server.ts b/server/aws-lsp-codewhisperer/src/shared/proxy-server.ts index 7313aacaba..274335ff1d 100644 --- a/server/aws-lsp-codewhisperer/src/shared/proxy-server.ts +++ b/server/aws-lsp-codewhisperer/src/shared/proxy-server.ts @@ -2,8 +2,10 @@ import { QAgenticChatServer } from '../language-server/agenticChat/qAgenticChatS import { SecurityScanServerToken } from '../language-server/securityScan/codeWhispererSecurityScanServer' import { CodewhispererServerFactory } from '../language-server/inline-completion/codeWhispererServer' import { QNetTransformServerToken } from '../language-server/netTransform/netTransformServer' +import { AtxNetTransformServerToken } from '../language-server/netTransform/atxNetTransformServer' import { QChatServerFactory } from '../language-server/chat/qChatServer' import { QConfigurationServerToken } from '../language-server/configuration/qConfigurationServer' +import { TransformConfigurationServerToken } from '../language-server/configuration/transformConfigurationServer' import { getOrThrowBaseTokenServiceManager } from './amazonQServiceManager/AmazonQTokenServiceManager' import { getOrThrowBaseIAMServiceManager } from './amazonQServiceManager/AmazonQIAMServiceManager' import { LocalProjectContextServer } from '../language-server/localProjectContext/localProjectContextServer' @@ -17,6 +19,8 @@ export const CodeWhispererSecurityScanServerTokenProxy = SecurityScanServerToken export const QNetTransformServerTokenProxy = QNetTransformServerToken() +export const AtxNetTransformServerTokenProxy = AtxNetTransformServerToken() + export const QChatServerTokenProxy = QChatServerFactory(getOrThrowBaseTokenServiceManager) export const QChatServerIAMProxy = QChatServerFactory(getOrThrowBaseIAMServiceManager) @@ -24,6 +28,8 @@ export const QAgenticChatServerProxy = QAgenticChatServer() export const QConfigurationServerTokenProxy = QConfigurationServerToken() +export const TransformConfigurationServerTokenProxy = TransformConfigurationServerToken() + export const QLocalProjectContextServerProxy = LocalProjectContextServer() export const WorkspaceContextServerTokenProxy = WorkspaceContextServer()