@aws/lsp-codewhisperer 0.0.28 → 0.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +3 -0
  3. package/out/client/token/codewhisperer.d.ts +1 -1
  4. package/out/constants.d.ts +3 -0
  5. package/out/constants.js +4 -1
  6. package/out/constants.js.map +1 -1
  7. package/out/language-server/amazonQServiceManager/AmazonQTokenServiceManager.d.ts +90 -0
  8. package/out/language-server/amazonQServiceManager/AmazonQTokenServiceManager.js +466 -0
  9. package/out/language-server/amazonQServiceManager/AmazonQTokenServiceManager.js.map +1 -0
  10. package/out/language-server/amazonQServiceManager/BaseAmazonQServiceManager.d.ts +5 -0
  11. package/out/language-server/amazonQServiceManager/BaseAmazonQServiceManager.js +3 -0
  12. package/out/language-server/amazonQServiceManager/BaseAmazonQServiceManager.js.map +1 -0
  13. package/out/language-server/amazonQServiceManager/errors.d.ts +28 -0
  14. package/out/language-server/amazonQServiceManager/errors.js +70 -0
  15. package/out/language-server/amazonQServiceManager/errors.js.map +1 -0
  16. package/out/language-server/amazonQServiceManager/qDeveloperProfiles.js +7 -7
  17. package/out/language-server/amazonQServiceManager/qDeveloperProfiles.js.map +1 -1
  18. package/out/language-server/chat/chatController.d.ts +1 -1
  19. package/out/language-server/chat/chatController.js +17 -1
  20. package/out/language-server/chat/chatController.js.map +1 -1
  21. package/out/language-server/chat/chatSessionManagementService.d.ts +3 -8
  22. package/out/language-server/chat/chatSessionManagementService.js +5 -57
  23. package/out/language-server/chat/chatSessionManagementService.js.map +1 -1
  24. package/out/language-server/chat/chatSessionService.d.ts +2 -3
  25. package/out/language-server/chat/chatSessionService.js +7 -21
  26. package/out/language-server/chat/chatSessionService.js.map +1 -1
  27. package/out/language-server/chat/contexts/triggerContext.d.ts +1 -1
  28. package/out/language-server/chat/contexts/triggerContext.js +2 -1
  29. package/out/language-server/chat/contexts/triggerContext.js.map +1 -1
  30. package/out/language-server/chat/telemetry/chatTelemetryController.js +88 -81
  31. package/out/language-server/chat/telemetry/chatTelemetryController.js.map +1 -1
  32. package/out/language-server/codeWhispererSecurityScanServer.d.ts +2 -4
  33. package/out/language-server/codeWhispererSecurityScanServer.js +24 -11
  34. package/out/language-server/codeWhispererSecurityScanServer.js.map +1 -1
  35. package/out/language-server/codeWhispererServer.js +90 -37
  36. package/out/language-server/codeWhispererServer.js.map +1 -1
  37. package/out/language-server/codeWhispererService.d.ts +8 -1
  38. package/out/language-server/codeWhispererService.js +39 -16
  39. package/out/language-server/codeWhispererService.js.map +1 -1
  40. package/out/language-server/configuration/qConfigurationServer.d.ts +5 -9
  41. package/out/language-server/configuration/qConfigurationServer.js +23 -35
  42. package/out/language-server/configuration/qConfigurationServer.js.map +1 -1
  43. package/out/language-server/netTransformServer.js +2 -2
  44. package/out/language-server/netTransformServer.js.map +1 -1
  45. package/out/language-server/proxy-server.js +3 -14
  46. package/out/language-server/proxy-server.js.map +1 -1
  47. package/out/language-server/qChatServer.d.ts +2 -4
  48. package/out/language-server/qChatServer.js +30 -16
  49. package/out/language-server/qChatServer.js.map +1 -1
  50. package/out/language-server/securityScan/constants.d.ts +3 -0
  51. package/out/language-server/securityScan/constants.js +7 -0
  52. package/out/language-server/securityScan/constants.js.map +1 -0
  53. package/out/language-server/securityScan/securityScanHandler.d.ts +5 -5
  54. package/out/language-server/securityScan/securityScanHandler.js +22 -12
  55. package/out/language-server/securityScan/securityScanHandler.js.map +1 -1
  56. package/out/language-server/telemetry/codeDiffTracker.d.ts +1 -1
  57. package/out/language-server/telemetry/codeDiffTracker.js +1 -1
  58. package/out/language-server/telemetry/codeDiffTracker.js.map +1 -1
  59. package/out/language-server/telemetryService.d.ts +7 -5
  60. package/out/language-server/telemetryService.js +29 -20
  61. package/out/language-server/telemetryService.js.map +1 -1
  62. package/out/language-server/testUtils.d.ts +3 -0
  63. package/out/language-server/testUtils.js +12 -1
  64. package/out/language-server/testUtils.js.map +1 -1
  65. package/out/language-server/utils.d.ts +2 -0
  66. package/out/language-server/utils.js +11 -0
  67. package/out/language-server/utils.js.map +1 -1
  68. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.29](https://github.com/aws/language-servers/compare/lsp-codewhisperer/v0.0.28...lsp-codewhisperer/v0.0.29) (2025-03-26)
4
+
5
+
6
+ ### Features
7
+
8
+ * abort all inflight requests when resetCodewhispererService is invoked ([#848](https://github.com/aws/language-servers/issues/848)) ([681889b](https://github.com/aws/language-servers/commit/681889bd40a0fb84ea624f177b07ef579864303a))
9
+ * added auth listener to reset the service manager state in case of bearer token signout ([#842](https://github.com/aws/language-servers/issues/842)) ([780be3f](https://github.com/aws/language-servers/commit/780be3fdb92917e58524472ea5967f405f802db5))
10
+ * **amazonq:** accept extra context for Q Inline Suggestions ([4a508df](https://github.com/aws/language-servers/commit/4a508dfcba714301145089263bdce8b8f18ec03b))
11
+ * **amazonq:** add eu-central-1 endpoint ([83133d6](https://github.com/aws/language-servers/commit/83133d61815c5acfba7ead1c87d0aaef206e72d4))
12
+ * **amazonq:** add regionalization support to security scan server ([#859](https://github.com/aws/language-servers/issues/859)) ([9945398](https://github.com/aws/language-servers/commit/99453989934849eddf1029763c22208cdb13be74))
13
+ * **amazonq:** add regionalization support to Telemetry service ([6937c7f](https://github.com/aws/language-servers/commit/6937c7fa53c94f23dab323d0cd92970edafd4452))
14
+ * **amazonq:** add support for setting profile to null ([b02906d](https://github.com/aws/language-servers/commit/b02906d04fad42f09e32d44120c4dd32cb2a649c))
15
+ * **amazonq:** attach profileArn to API calls when available ([00fe7e2](https://github.com/aws/language-servers/commit/00fe7e2d1327b519042480b8216d663a48dced54))
16
+ * **amazonq:** Cancel inflight updateProfile requests ([#861](https://github.com/aws/language-servers/issues/861)) ([a4a4309](https://github.com/aws/language-servers/commit/a4a4309ef1f7c0978aa44a4063d1e8160ad53bb6))
17
+ * **amazonq:** centralize access to Q Service SDK instance and add support for Q Developer profiles ([#814](https://github.com/aws/language-servers/issues/814)) ([5f11549](https://github.com/aws/language-servers/commit/5f11549bb37acf3788c991a4ceeb38a7b17f1324))
18
+ * **amazonq:** integrate q service manager with configuration server ([#852](https://github.com/aws/language-servers/issues/852)) ([c0e3290](https://github.com/aws/language-servers/commit/c0e32905e5940a79f59b19913aac9f989e85f8fe))
19
+ * **amazonq:** intergrate regionalization support into Q Chat server ([#853](https://github.com/aws/language-servers/issues/853)) ([394104c](https://github.com/aws/language-servers/commit/394104c3702055f55ababbbfb056bf7904f5115e))
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * **amazonq:** await for recordMetric in CodeDiff tracker ([ee04afc](https://github.com/aws/language-servers/commit/ee04afc7775e83bfa3868b4b661cf59ff3c7f949))
25
+ * **amazonq:** handle exceptions in TelemetryService ([e8f6375](https://github.com/aws/language-servers/commit/e8f637524fe878c26c72f506de4abea86b481fde))
26
+ * **amazonq:** specify code analysis scope and scan name when running scans ([#858](https://github.com/aws/language-servers/issues/858)) ([a925297](https://github.com/aws/language-servers/commit/a925297aabc89334f4f9eed6c13146f4fd20b164))
27
+ * update @aws/language-server-runtimes to 0.2.48 ([e1f620a](https://github.com/aws/language-servers/commit/e1f620ac2b59b4f61daff842a9f29ded1b8fa04e))
28
+
3
29
  ## [0.0.28](https://github.com/aws/language-servers/compare/lsp-codewhisperer/v0.0.27...lsp-codewhisperer/v0.0.28) (2025-03-18)
4
30
 
5
31
 
package/README.md CHANGED
@@ -91,3 +91,6 @@ const params: InitializeParams = {
91
91
  }
92
92
  }
93
93
  ```
94
+
95
+ ### Extra context for Q Inline Suggestions
96
+ In cases when the client runs in a specific environment that requires customized suggestions, the server supports a `aws.q.inlineSuggestions.extraContext` workspace configuration. This extra context will be passed to the left file content of the request in the beginning of the file.
@@ -2,7 +2,7 @@ import { AWSError } from 'aws-sdk';
2
2
  import { ServiceConfigurationOptions } from 'aws-sdk/lib/service';
3
3
  import CodeWhispererClient = require('./codewhispererbearertokenclient');
4
4
  import { SDKInitializator } from '@aws/language-server-runtimes/server-interface';
5
- interface RequestExtras {
5
+ export interface RequestExtras {
6
6
  readonly service: AWS.Service;
7
7
  readonly operation: string;
8
8
  readonly params?: any;
@@ -2,4 +2,7 @@ export declare const DEFAULT_AWS_Q_ENDPOINT_URL = "https://codewhisperer.us-east
2
2
  export declare const DEFAULT_AWS_Q_REGION = "us-east-1";
3
3
  export declare const AWS_Q_ENDPOINTS: {
4
4
  "us-east-1": string;
5
+ 'eu-central-1': string;
5
6
  };
7
+ export declare const AWS_Q_REGION_ENV_VAR = "AWS_Q_REGION";
8
+ export declare const AWS_Q_ENDPOINT_URL_ENV_VAR = "AWS_Q_ENDPOINT_URL";
package/out/constants.js CHANGED
@@ -1,9 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AWS_Q_ENDPOINTS = exports.DEFAULT_AWS_Q_REGION = exports.DEFAULT_AWS_Q_ENDPOINT_URL = void 0;
3
+ exports.AWS_Q_ENDPOINT_URL_ENV_VAR = exports.AWS_Q_REGION_ENV_VAR = exports.AWS_Q_ENDPOINTS = exports.DEFAULT_AWS_Q_REGION = exports.DEFAULT_AWS_Q_ENDPOINT_URL = void 0;
4
4
  exports.DEFAULT_AWS_Q_ENDPOINT_URL = 'https://codewhisperer.us-east-1.amazonaws.com/';
5
5
  exports.DEFAULT_AWS_Q_REGION = 'us-east-1';
6
6
  exports.AWS_Q_ENDPOINTS = {
7
7
  [exports.DEFAULT_AWS_Q_REGION]: exports.DEFAULT_AWS_Q_ENDPOINT_URL,
8
+ 'eu-central-1': 'https://q.eu-central-1.amazonaws.com/',
8
9
  };
10
+ exports.AWS_Q_REGION_ENV_VAR = 'AWS_Q_REGION';
11
+ exports.AWS_Q_ENDPOINT_URL_ENV_VAR = 'AWS_Q_ENDPOINT_URL';
9
12
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,0BAA0B,GAAG,gDAAgD,CAAA;AAC7E,QAAA,oBAAoB,GAAG,WAAW,CAAA;AAElC,QAAA,eAAe,GAAG;IAC3B,CAAC,4BAAoB,CAAC,EAAE,kCAA0B;CACrD,CAAA"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,0BAA0B,GAAG,gDAAgD,CAAA;AAC7E,QAAA,oBAAoB,GAAG,WAAW,CAAA;AAElC,QAAA,eAAe,GAAG;IAC3B,CAAC,4BAAoB,CAAC,EAAE,kCAA0B;IAClD,cAAc,EAAE,uCAAuC;CAC1D,CAAA;AAEY,QAAA,oBAAoB,GAAG,cAAc,CAAA;AACrC,QAAA,0BAA0B,GAAG,oBAAoB,CAAA"}
@@ -0,0 +1,90 @@
1
+ import { Logging, Lsp, CredentialsProvider, SDKInitializator, Runtime, Workspace, SsoConnectionType } from '@aws/language-server-runtimes/server-interface';
2
+ import { CodeWhispererServiceToken } from '../codeWhispererService';
3
+ import { BaseAmazonQServiceManager } from './BaseAmazonQServiceManager';
4
+ import { CodeWhispererStreaming } from '@amzn/codewhisperer-streaming';
5
+ export interface Features {
6
+ lsp: Lsp;
7
+ logging: Logging;
8
+ runtime: Runtime;
9
+ credentialsProvider: CredentialsProvider;
10
+ sdkInitializator: SDKInitializator;
11
+ workspace: Workspace;
12
+ }
13
+ /**
14
+ * AmazonQTokenServiceManager manages state and provides centralized access to
15
+ * instance of CodeWhispererServiceToken SDK client to any consuming code.
16
+ * It ensures that CodeWhispererServiceToken is configured to always access correct regionalized Amazon Q Developer API endpoint.
17
+ * Regional endppoint is selected based on:
18
+ * 1) current SSO auth connection type (BuilderId or IDC).
19
+ * 2) selected Amazon Q Developer profile (only for IDC connection type).
20
+ *
21
+ * @states
22
+ * - PENDING_CONNECTION: Initial state when no bearer token is set
23
+ * - PENDING_Q_PROFILE: When using Identity Center and waiting for profile selection
24
+ * - PENDING_Q_PROFILE_UPDATE: During profile update operation
25
+ * - INITIALIZED: Service is ready to handle requests
26
+ *
27
+ * @connectionTypes
28
+ * - none: No active connection
29
+ * - builderId: Connected via Builder ID
30
+ * - identityCenter: Connected via Identity Center
31
+ *
32
+ * AmazonQTokenServiceManager is a singleton class, which must be instantiated with Language Server runtimes [Features](https://github.com/aws/language-server-runtimes/blob/21d5d1dc7c73499475b7c88c98d2ce760e5d26c8/runtimes/server-interface/server.ts#L31-L42)
33
+ * To get access to current CodeWhispererServiceToken client object, call `getCodewhispererService()` mathod:
34
+ *
35
+ * @example
36
+ * const AmazonQServiceManager = AmazonQTokenServiceManager.getInstance(features);
37
+ * const codewhispererService = AmazonQServiceManager.getCodewhispererService();
38
+ */
39
+ export declare class AmazonQTokenServiceManager implements BaseAmazonQServiceManager {
40
+ private static instance;
41
+ private features;
42
+ private logging;
43
+ private cachedCodewhispererService?;
44
+ private enableDeveloperProfileSupport?;
45
+ private configurationCache;
46
+ private activeIdcProfile?;
47
+ private connectionType?;
48
+ private profileChangeTokenSource;
49
+ private region?;
50
+ private endpoint?;
51
+ /**
52
+ * Internal state of Service connection, based on status of bearer token and Amazon Q Developer profile selection.
53
+ * Supported states:
54
+ * PENDING_CONNECTION - Waiting for Bearer Token and StartURL to be passed
55
+ * PENDING_Q_PROFILE - (only for identityCenter connection) waiting for setting Developer Profile
56
+ * PENDING_Q_PROFILE_UPDATE (only for identityCenter connection) waiting for Developer Profile to complete
57
+ * INITIALIZED - Service is initialized
58
+ */
59
+ private state;
60
+ private constructor();
61
+ static getInstance(features: Features): AmazonQTokenServiceManager;
62
+ private initialize;
63
+ private setupAuthListener;
64
+ private setupConfigurationListeners;
65
+ /**
66
+ * Validate if Bearer Token Connection type has changed mid-session.
67
+ * When connection type change is detected: reinitialize CodeWhispererService class with current connection type.
68
+ */
69
+ private handleSsoConnectionChange;
70
+ handleDidChangeConfiguration(): Promise<void>;
71
+ private cancelActiveProfileChangeToken;
72
+ private handleTokenCancellationRequest;
73
+ private handleProfileChange;
74
+ getCodewhispererService(): CodeWhispererServiceToken;
75
+ getStreamingClient(): CodeWhispererStreaming;
76
+ private resetCodewhispererService;
77
+ private createCodewhispererServiceInstances;
78
+ private getCustomUserAgent;
79
+ private serviceFactory;
80
+ private streamingClientFactory;
81
+ private log;
82
+ private logServiceState;
83
+ static resetInstance(): void;
84
+ getState(): "PENDING_CONNECTION" | "PENDING_Q_PROFILE" | "PENDING_Q_PROFILE_UPDATE" | "INITIALIZED";
85
+ getConnectionType(): SsoConnectionType | undefined;
86
+ getActiveProfileArn(): string | undefined;
87
+ setServiceFactory(factory: (region: string, endpoint: string) => CodeWhispererServiceToken): void;
88
+ getServiceFactory(): (region: string, endpoint: string) => CodeWhispererServiceToken;
89
+ getEnableDeveloperProfileSupport(): boolean;
90
+ }
@@ -0,0 +1,466 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AmazonQTokenServiceManager = void 0;
4
+ const server_interface_1 = require("@aws/language-server-runtimes/server-interface");
5
+ const constants_1 = require("../../constants");
6
+ const codeWhispererService_1 = require("../codeWhispererService");
7
+ const errors_1 = require("./errors");
8
+ const qConfigurationServer_1 = require("../configuration/qConfigurationServer");
9
+ const lsp_core_1 = require("@aws/lsp-core");
10
+ const codewhisperer_streaming_1 = require("@amzn/codewhisperer-streaming");
11
+ const util_retry_1 = require("@aws-sdk/util-retry");
12
+ const qDeveloperProfiles_1 = require("./qDeveloperProfiles");
13
+ const telemetryUtils_1 = require("../utilities/telemetryUtils");
14
+ const utils_1 = require("../utils");
15
+ /**
16
+ * AmazonQTokenServiceManager manages state and provides centralized access to
17
+ * instance of CodeWhispererServiceToken SDK client to any consuming code.
18
+ * It ensures that CodeWhispererServiceToken is configured to always access correct regionalized Amazon Q Developer API endpoint.
19
+ * Regional endppoint is selected based on:
20
+ * 1) current SSO auth connection type (BuilderId or IDC).
21
+ * 2) selected Amazon Q Developer profile (only for IDC connection type).
22
+ *
23
+ * @states
24
+ * - PENDING_CONNECTION: Initial state when no bearer token is set
25
+ * - PENDING_Q_PROFILE: When using Identity Center and waiting for profile selection
26
+ * - PENDING_Q_PROFILE_UPDATE: During profile update operation
27
+ * - INITIALIZED: Service is ready to handle requests
28
+ *
29
+ * @connectionTypes
30
+ * - none: No active connection
31
+ * - builderId: Connected via Builder ID
32
+ * - identityCenter: Connected via Identity Center
33
+ *
34
+ * AmazonQTokenServiceManager is a singleton class, which must be instantiated with Language Server runtimes [Features](https://github.com/aws/language-server-runtimes/blob/21d5d1dc7c73499475b7c88c98d2ce760e5d26c8/runtimes/server-interface/server.ts#L31-L42)
35
+ * To get access to current CodeWhispererServiceToken client object, call `getCodewhispererService()` mathod:
36
+ *
37
+ * @example
38
+ * const AmazonQServiceManager = AmazonQTokenServiceManager.getInstance(features);
39
+ * const codewhispererService = AmazonQServiceManager.getCodewhispererService();
40
+ */
41
+ class AmazonQTokenServiceManager {
42
+ static instance = null;
43
+ features;
44
+ logging;
45
+ cachedCodewhispererService;
46
+ enableDeveloperProfileSupport;
47
+ configurationCache = new Map();
48
+ activeIdcProfile;
49
+ connectionType;
50
+ profileChangeTokenSource;
51
+ region;
52
+ endpoint;
53
+ /**
54
+ * Internal state of Service connection, based on status of bearer token and Amazon Q Developer profile selection.
55
+ * Supported states:
56
+ * PENDING_CONNECTION - Waiting for Bearer Token and StartURL to be passed
57
+ * PENDING_Q_PROFILE - (only for identityCenter connection) waiting for setting Developer Profile
58
+ * PENDING_Q_PROFILE_UPDATE (only for identityCenter connection) waiting for Developer Profile to complete
59
+ * INITIALIZED - Service is initialized
60
+ */
61
+ state = 'PENDING_CONNECTION';
62
+ constructor() { }
63
+ static getInstance(features) {
64
+ if (!AmazonQTokenServiceManager.instance) {
65
+ AmazonQTokenServiceManager.instance = new AmazonQTokenServiceManager();
66
+ AmazonQTokenServiceManager.instance.initialize(features);
67
+ }
68
+ return AmazonQTokenServiceManager.instance;
69
+ }
70
+ initialize(features) {
71
+ if (!features || !features.logging || !features.lsp) {
72
+ throw new errors_1.AmazonQServiceInitializationError('Service features not initialized. Please ensure proper initialization.');
73
+ }
74
+ this.features = features;
75
+ this.logging = features.logging;
76
+ if (!this.features.lsp.getClientInitializeParams()) {
77
+ this.log('AmazonQTokenServiceManager initialized before LSP connection was initialized.');
78
+ throw new errors_1.AmazonQServiceInitializationError('AmazonQTokenServiceManager initialized before LSP connection was initialized.');
79
+ }
80
+ // Bind methods that are passed by reference to some handlers to maintain proper scope.
81
+ this.serviceFactory = this.serviceFactory.bind(this);
82
+ this.handleDidChangeConfiguration = this.handleDidChangeConfiguration.bind(this);
83
+ this.log('Reading enableDeveloperProfileSupport setting from AWSInitializationOptions');
84
+ if (this.features.lsp.getClientInitializeParams()?.initializationOptions?.aws) {
85
+ const awsOptions = this.features.lsp.getClientInitializeParams()?.initializationOptions?.aws || {};
86
+ this.enableDeveloperProfileSupport = (0, qDeveloperProfiles_1.signalsAWSQDeveloperProfilesEnabled)(awsOptions);
87
+ this.log(`Enabled Q Developer Profile support: ${this.enableDeveloperProfileSupport}`);
88
+ }
89
+ this.connectionType = 'none';
90
+ this.state = 'PENDING_CONNECTION';
91
+ this.setupAuthListener();
92
+ this.setupConfigurationListeners();
93
+ this.log('Manager instance is initialize');
94
+ }
95
+ setupAuthListener() {
96
+ this.features.credentialsProvider.onCredentialsDeleted((type) => {
97
+ this.log(`Received credentials delete event for type: ${type}`);
98
+ if (type === 'iam') {
99
+ return;
100
+ }
101
+ this.cancelActiveProfileChangeToken();
102
+ this.resetCodewhispererService();
103
+ this.connectionType = 'none';
104
+ this.state = 'PENDING_CONNECTION';
105
+ });
106
+ }
107
+ setupConfigurationListeners() {
108
+ // TODO: refactor how handleDidChangeConfiguration can be hooked to lsp Feature.
109
+ // Currently can't do this, as it overrides handler set by Server, using ServiceManager.
110
+ // this.features.lsp.onInitialized(this.handleDidChangeConfiguration)
111
+ // this.features.lsp.didChangeConfiguration(this.handleDidChangeConfiguration)
112
+ this.features.lsp.workspace.onUpdateConfiguration(async (params, _token) => {
113
+ try {
114
+ if (params.section === qConfigurationServer_1.Q_CONFIGURATION_SECTION && params.settings.profileArn !== undefined) {
115
+ const profileArn = params.settings.profileArn;
116
+ if (!(0, utils_1.isStringOrNull)(profileArn)) {
117
+ throw new Error('Expected params.settings.profileArn to be of either type string or null');
118
+ }
119
+ this.log(`Profile update is requested for profile ${profileArn}`);
120
+ this.cancelActiveProfileChangeToken();
121
+ this.profileChangeTokenSource = new server_interface_1.CancellationTokenSource();
122
+ await this.handleProfileChange(profileArn, this.profileChangeTokenSource.token);
123
+ }
124
+ }
125
+ catch (error) {
126
+ this.log('Error updating profiles: ' + error);
127
+ if (error instanceof errors_1.AmazonQServiceProfileUpdateCancelled) {
128
+ throw new server_interface_1.ResponseError(server_interface_1.LSPErrorCodes.ServerCancelled, error.message, {
129
+ awsErrorCode: error.code,
130
+ });
131
+ }
132
+ if (error instanceof errors_1.AmazonQError) {
133
+ throw new server_interface_1.ResponseError(server_interface_1.LSPErrorCodes.RequestFailed, error.message, {
134
+ awsErrorCode: error.code,
135
+ });
136
+ }
137
+ throw new server_interface_1.ResponseError(server_interface_1.LSPErrorCodes.RequestFailed, 'Failed to update configuration');
138
+ }
139
+ finally {
140
+ if (this.profileChangeTokenSource) {
141
+ this.profileChangeTokenSource.dispose();
142
+ this.profileChangeTokenSource = undefined;
143
+ }
144
+ }
145
+ });
146
+ }
147
+ /**
148
+ * Validate if Bearer Token Connection type has changed mid-session.
149
+ * When connection type change is detected: reinitialize CodeWhispererService class with current connection type.
150
+ */
151
+ handleSsoConnectionChange() {
152
+ const newConnectionType = this.features.credentialsProvider.getConnectionType();
153
+ this.logServiceState('Validate State of SSO Connection');
154
+ if (newConnectionType === 'none' || !this.features.credentialsProvider.hasCredentials('bearer')) {
155
+ // Connection was reset, wait for SSO connection token from client
156
+ this.log('No active SSO connection is detected, resetting the client');
157
+ this.resetCodewhispererService();
158
+ this.connectionType = 'none';
159
+ this.state = 'PENDING_CONNECTION';
160
+ return;
161
+ }
162
+ // Connection type hasn't change.
163
+ if (newConnectionType === this.connectionType) {
164
+ this.log('Connection type did not change.');
165
+ return;
166
+ }
167
+ // Connection type changed to 'builderId'
168
+ if (newConnectionType === 'builderId') {
169
+ this.log('Detected New connection type: builderId');
170
+ this.resetCodewhispererService();
171
+ // For the builderId connection type regional endpoint discovery chain is:
172
+ // region set by client -> runtime region -> default region
173
+ const clientParams = this.features.lsp.getClientInitializeParams();
174
+ this.createCodewhispererServiceInstances('builderId', clientParams?.initializationOptions?.aws?.region);
175
+ this.state = 'INITIALIZED';
176
+ this.log('Initialized Amazon Q service with builderId connection');
177
+ return;
178
+ }
179
+ // Connection type changed to 'identityCenter'
180
+ if (newConnectionType === 'identityCenter') {
181
+ this.log('Detected New connection type: identityCenter');
182
+ this.resetCodewhispererService();
183
+ if (this.enableDeveloperProfileSupport) {
184
+ this.connectionType = 'identityCenter';
185
+ this.state = 'PENDING_Q_PROFILE';
186
+ this.logServiceState('Pending profile selection for IDC connection');
187
+ return;
188
+ }
189
+ this.createCodewhispererServiceInstances('identityCenter');
190
+ this.state = 'INITIALIZED';
191
+ this.log('Initialized Amazon Q service with identityCenter connection');
192
+ return;
193
+ }
194
+ this.logServiceState('Unknown Connection state');
195
+ }
196
+ async handleDidChangeConfiguration() {
197
+ try {
198
+ const qConfig = await this.features.lsp.workspace.getConfiguration(qConfigurationServer_1.Q_CONFIGURATION_SECTION);
199
+ if (qConfig) {
200
+ // aws.q.customizationArn - selected customization
201
+ const customizationArn = lsp_core_1.textUtils.undefinedIfEmpty(qConfig.customization);
202
+ this.log(`Read configuration customizationArn=${customizationArn}`);
203
+ this.configurationCache.set('customizationArn', customizationArn);
204
+ // aws.q.optOutTelemetry - telemetry optout option
205
+ const optOutTelemetryPreference = qConfig['optOutTelemetry'] === true ? 'OPTOUT' : 'OPTIN';
206
+ this.log(`Read configuration optOutTelemetryPreference=${optOutTelemetryPreference}`);
207
+ this.configurationCache.set('optOutTelemetryPreference', optOutTelemetryPreference);
208
+ if (this.cachedCodewhispererService) {
209
+ this.log(`Using customization=${customizationArn}`);
210
+ this.cachedCodewhispererService.customizationArn = customizationArn;
211
+ }
212
+ }
213
+ const codeWhispererConfig = await this.features.lsp.workspace.getConfiguration('aws.codeWhisperer');
214
+ if (codeWhispererConfig) {
215
+ // aws.codeWhisperer.includeSuggestionsWithCodeReferences - return suggestions with code references
216
+ const includeSuggestionsWithCodeReferences = codeWhispererConfig['includeSuggestionsWithCodeReferences'] === true;
217
+ this.log(`Read сonfiguration includeSuggestionsWithCodeReferences=${includeSuggestionsWithCodeReferences}`);
218
+ this.configurationCache.set('includeSuggestionsWithCodeReferences', includeSuggestionsWithCodeReferences);
219
+ // aws.codeWhisperershareCodeWhispererContentWithAWS - share content with AWS
220
+ const shareCodeWhispererContentWithAWS = codeWhispererConfig['shareCodeWhispererContentWithAWS'] === true;
221
+ this.log(`Read configuration shareCodeWhispererContentWithAWS=${shareCodeWhispererContentWithAWS}`);
222
+ this.configurationCache.set('shareCodeWhispererContentWithAWS', shareCodeWhispererContentWithAWS);
223
+ if (this.cachedCodewhispererService) {
224
+ this.log('Update shareCodeWhispererContentWithAWS setting on cachedCodewhispererService to ' +
225
+ shareCodeWhispererContentWithAWS);
226
+ this.cachedCodewhispererService.shareCodeWhispererContentWithAWS = shareCodeWhispererContentWithAWS;
227
+ }
228
+ }
229
+ }
230
+ catch (error) {
231
+ this.log(`Error in GetConfiguration: ${error}`);
232
+ }
233
+ }
234
+ cancelActiveProfileChangeToken() {
235
+ this.profileChangeTokenSource?.cancel();
236
+ this.profileChangeTokenSource?.dispose();
237
+ this.profileChangeTokenSource = undefined;
238
+ }
239
+ handleTokenCancellationRequest(token) {
240
+ if (token.isCancellationRequested) {
241
+ this.logServiceState('Handling CancellationToken cancellation request');
242
+ throw new errors_1.AmazonQServiceProfileUpdateCancelled('Requested profile update got cancelled');
243
+ }
244
+ }
245
+ async handleProfileChange(newProfileArn, token) {
246
+ if (!this.enableDeveloperProfileSupport) {
247
+ this.log('Developer Profiles Support is not enabled');
248
+ return;
249
+ }
250
+ if (typeof newProfileArn === 'string' && newProfileArn.length === 0) {
251
+ throw new Error('Received invalid Profile ARN (empty string)');
252
+ }
253
+ this.logServiceState('UpdateProfile is requested');
254
+ // Test if connection type changed
255
+ this.handleSsoConnectionChange();
256
+ if (this.connectionType === 'none') {
257
+ throw new errors_1.AmazonQServicePendingSigninError();
258
+ }
259
+ if (this.connectionType !== 'identityCenter') {
260
+ this.logServiceState('Q Profile can not be set');
261
+ throw new errors_1.AmazonQServiceNoProfileSupportError(`Connection type ${this.connectionType} does not support Developer Profiles feature.`);
262
+ }
263
+ if ((this.state === 'INITIALIZED' && this.activeIdcProfile) || this.state === 'PENDING_Q_PROFILE') {
264
+ // Change status to pending to prevent API calls until profile is updated.
265
+ // Because `listAvailableProfiles` below can take few seconds to complete,
266
+ // there is possibility that client could send requests while profile is changing.
267
+ this.state = 'PENDING_Q_PROFILE_UPDATE';
268
+ }
269
+ // Client sent an explicit null, indicating they want to reset the assigned profile (if any)
270
+ if (newProfileArn === null) {
271
+ this.logServiceState('Received null profile, resetting to PENDING_Q_PROFILE state');
272
+ this.resetCodewhispererService();
273
+ this.state = 'PENDING_Q_PROFILE';
274
+ return;
275
+ }
276
+ const profiles = await (0, qDeveloperProfiles_1.getListAllAvailableProfilesHandler)(this.serviceFactory)({
277
+ connectionType: 'identityCenter',
278
+ logging: this.logging,
279
+ });
280
+ this.handleTokenCancellationRequest(token);
281
+ const newProfile = profiles.find(el => el.arn === newProfileArn);
282
+ if (!newProfile || !newProfile.identityDetails?.region) {
283
+ this.log(`Amazon Q Profile ${newProfileArn} is not valid`);
284
+ this.resetCodewhispererService();
285
+ this.state = 'PENDING_Q_PROFILE';
286
+ throw new errors_1.AmazonQServiceInvalidProfileError('Requested Amazon Q Profile does not exist');
287
+ }
288
+ this.handleTokenCancellationRequest(token);
289
+ if (!this.activeIdcProfile) {
290
+ this.activeIdcProfile = newProfile;
291
+ this.createCodewhispererServiceInstances('identityCenter', newProfile.identityDetails.region);
292
+ this.state = 'INITIALIZED';
293
+ this.log(`Initialized identityCenter connection to region ${newProfile.identityDetails.region} for profile ${newProfile.arn}`);
294
+ return;
295
+ }
296
+ // Profile didn't change
297
+ if (this.activeIdcProfile && this.activeIdcProfile.arn === newProfile.arn) {
298
+ // Update cached profile fields, keep existing client
299
+ this.log(`Profile selection did not change, active profile is ${this.activeIdcProfile.arn}`);
300
+ this.activeIdcProfile = newProfile;
301
+ this.state = 'INITIALIZED';
302
+ return;
303
+ }
304
+ this.handleTokenCancellationRequest(token);
305
+ // At this point new valid profile is selected.
306
+ const oldRegion = this.activeIdcProfile.identityDetails?.region;
307
+ const newRegion = newProfile.identityDetails.region;
308
+ if (oldRegion === newRegion) {
309
+ this.log(`New profile is in the same region as old one, keeping exising service.`);
310
+ this.log(`New active profile is ${this.activeIdcProfile.arn}, region ${oldRegion}`);
311
+ this.activeIdcProfile = newProfile;
312
+ this.state = 'INITIALIZED';
313
+ if (this.cachedCodewhispererService) {
314
+ this.cachedCodewhispererService.profileArn = newProfile.arn;
315
+ }
316
+ return;
317
+ }
318
+ this.log(`Switching service client region from ${oldRegion} to ${newRegion}`);
319
+ this.handleTokenCancellationRequest(token);
320
+ // Selected new profile is in different region. Re-initialize service
321
+ this.resetCodewhispererService();
322
+ this.activeIdcProfile = newProfile;
323
+ this.createCodewhispererServiceInstances('identityCenter', newProfile.identityDetails.region);
324
+ this.state = 'INITIALIZED';
325
+ return;
326
+ }
327
+ getCodewhispererService() {
328
+ // Prevent initiating requests while profile change is in progress.
329
+ if (this.state === 'PENDING_Q_PROFILE_UPDATE') {
330
+ throw new errors_1.AmazonQServicePendingProfileUpdateError();
331
+ }
332
+ this.handleSsoConnectionChange();
333
+ if (this.state === 'INITIALIZED' && this.cachedCodewhispererService) {
334
+ return this.cachedCodewhispererService;
335
+ }
336
+ if (this.state === 'PENDING_CONNECTION') {
337
+ throw new errors_1.AmazonQServicePendingSigninError();
338
+ }
339
+ if (this.state === 'PENDING_Q_PROFILE') {
340
+ throw new errors_1.AmazonQServicePendingProfileError();
341
+ }
342
+ throw new errors_1.AmazonQServiceNotInitializedError();
343
+ }
344
+ getStreamingClient() {
345
+ this.log('Getting instance of CodeWhispererStreaming client');
346
+ // Trigger checks in token service
347
+ const tokenService = this.getCodewhispererService();
348
+ if (!tokenService || !this.region || !this.endpoint) {
349
+ throw new errors_1.AmazonQServiceNotInitializedError();
350
+ }
351
+ return this.streamingClientFactory(this.region, this.endpoint);
352
+ }
353
+ resetCodewhispererService() {
354
+ this.cachedCodewhispererService?.abortInflightRequests();
355
+ this.cachedCodewhispererService = undefined;
356
+ this.activeIdcProfile = undefined;
357
+ }
358
+ createCodewhispererServiceInstances(connectionType, region) {
359
+ this.logServiceState('Initializing CodewhispererService');
360
+ let awsQRegion;
361
+ let awsQEndpoint;
362
+ if (region) {
363
+ this.log(`Selecting region (found: ${region}) provided by ${connectionType === 'builderId' ? 'client' : 'profile'}`);
364
+ awsQRegion = region;
365
+ // @ts-ignore
366
+ awsQEndpoint = constants_1.AWS_Q_ENDPOINTS[awsQRegion];
367
+ }
368
+ else {
369
+ const runtimeRegion = this.features.runtime.getConfiguration(constants_1.AWS_Q_REGION_ENV_VAR);
370
+ if (runtimeRegion) {
371
+ this.log(`Selecting region (found: ${runtimeRegion}) provided by runtime`);
372
+ awsQRegion = runtimeRegion;
373
+ // prettier-ignore
374
+ awsQEndpoint = // @ts-ignore
375
+ this.features.runtime.getConfiguration(constants_1.AWS_Q_ENDPOINT_URL_ENV_VAR) ?? constants_1.AWS_Q_ENDPOINTS[awsQRegion];
376
+ }
377
+ else {
378
+ this.log('Region not provided by caller or runtime, falling back to default region and endpoint');
379
+ awsQRegion = constants_1.DEFAULT_AWS_Q_REGION;
380
+ awsQEndpoint = constants_1.DEFAULT_AWS_Q_ENDPOINT_URL;
381
+ }
382
+ }
383
+ if (!awsQEndpoint) {
384
+ this.log(`Unable to determine endpoint (found: ${awsQEndpoint}) for region: ${awsQRegion}, falling back to default region and endpoint`);
385
+ awsQRegion = constants_1.DEFAULT_AWS_Q_REGION;
386
+ awsQEndpoint = constants_1.DEFAULT_AWS_Q_ENDPOINT_URL;
387
+ }
388
+ // Cache active region and endpoint selection
389
+ this.connectionType = connectionType;
390
+ this.region = awsQRegion;
391
+ this.endpoint = awsQEndpoint;
392
+ this.cachedCodewhispererService = this.serviceFactory(awsQRegion, awsQEndpoint);
393
+ this.log(`CodeWhispererToken service for connection type ${connectionType} was initialized, region=${awsQRegion}`);
394
+ this.logServiceState('CodewhispererService Initialization finished');
395
+ }
396
+ getCustomUserAgent() {
397
+ const initializeParams = this.features.lsp.getClientInitializeParams() || {};
398
+ return (0, telemetryUtils_1.getUserAgent)(initializeParams, this.features.runtime.serverInfo);
399
+ }
400
+ serviceFactory(region, endpoint) {
401
+ const service = new codeWhispererService_1.CodeWhispererServiceToken(this.features.credentialsProvider, this.features.workspace, region, endpoint, this.features.sdkInitializator);
402
+ const customUserAgent = this.getCustomUserAgent();
403
+ service.updateClientConfig({
404
+ customUserAgent: customUserAgent,
405
+ });
406
+ service.customizationArn = lsp_core_1.textUtils.undefinedIfEmpty(this.configurationCache.get('customizationArn'));
407
+ service.profileArn = this.activeIdcProfile?.arn;
408
+ service.shareCodeWhispererContentWithAWS =
409
+ this.configurationCache.get('shareCodeWhispererContentWithAWS') === true;
410
+ this.log('Configured CodeWhispererServiceToken instance settings:');
411
+ this.log(`customUserAgent=${customUserAgent}, customizationArn=${service.customizationArn}, shareCodeWhispererContentWithAWS=${service.shareCodeWhispererContentWithAWS}`);
412
+ return service;
413
+ }
414
+ streamingClientFactory(region, endpoint) {
415
+ const token = (0, utils_1.getBearerTokenFromProvider)(this.features.credentialsProvider);
416
+ // TODO: Follow-up with creating CodeWhispererStreaming client which supports inplace access to CredentialsProvider instead of caching static value.
417
+ // Without this, we need more complex mechanism for managing token change state when caching streaming client.
418
+ const streamingClient = this.features.sdkInitializator(codewhisperer_streaming_1.CodeWhispererStreaming, {
419
+ region,
420
+ endpoint,
421
+ token: { token: token },
422
+ retryStrategy: new util_retry_1.ConfiguredRetryStrategy(0, (attempt) => 500 + attempt ** 10),
423
+ customUserAgent: this.getCustomUserAgent(),
424
+ });
425
+ this.logging.debug(`Created streaming client instance region=${region}, endpoint=${endpoint}`);
426
+ return streamingClient;
427
+ }
428
+ log(message) {
429
+ const prefix = 'Amazon Q Token Service Manager';
430
+ this.logging?.log(`${prefix}: ${message}`);
431
+ }
432
+ logServiceState(context) {
433
+ this.logging?.debug(JSON.stringify({
434
+ context,
435
+ state: {
436
+ serviceStatus: this.state,
437
+ connectionType: this.connectionType,
438
+ activeIdcProfile: this.activeIdcProfile,
439
+ },
440
+ }));
441
+ }
442
+ // For Unit Tests
443
+ static resetInstance() {
444
+ AmazonQTokenServiceManager.instance = null;
445
+ }
446
+ getState() {
447
+ return this.state;
448
+ }
449
+ getConnectionType() {
450
+ return this.connectionType;
451
+ }
452
+ getActiveProfileArn() {
453
+ return this.activeIdcProfile?.arn;
454
+ }
455
+ setServiceFactory(factory) {
456
+ this.serviceFactory = factory.bind(this);
457
+ }
458
+ getServiceFactory() {
459
+ return this.serviceFactory;
460
+ }
461
+ getEnableDeveloperProfileSupport() {
462
+ return this.enableDeveloperProfileSupport === undefined ? false : this.enableDeveloperProfileSupport;
463
+ }
464
+ }
465
+ exports.AmazonQTokenServiceManager = AmazonQTokenServiceManager;
466
+ //# sourceMappingURL=AmazonQTokenServiceManager.js.map