Skip to content

Commit ea148cd

Browse files
Merge master into feature/hybridChat-local
2 parents cd2ce32 + 6898221 commit ea148cd

File tree

7 files changed

+179
-170
lines changed

7 files changed

+179
-170
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "Fix Error: 'Amazon Q service is not signed in'"
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "Fix Error: 'Amazon Q Profile is not selected for IDC connection type'"
4+
}

packages/amazonq/src/lsp/auth.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { AuthUtil } from 'aws-core-vscode/codewhisperer'
1818
import { Writable } from 'stream'
1919
import { onceChanged } from 'aws-core-vscode/utils'
2020
import { getLogger, oneMinute } from 'aws-core-vscode/shared'
21+
import { isSsoConnection } from 'aws-core-vscode/auth'
2122

2223
export const encryptionKey = crypto.randomBytes(32)
2324

@@ -76,8 +77,8 @@ export class AmazonQLspAuth {
7677
* @param force bypass memoization, and forcefully update the bearer token
7778
*/
7879
async refreshConnection(force: boolean = false) {
79-
const activeConnection = this.authUtil.auth.activeConnection
80-
if (activeConnection?.state === 'valid' && activeConnection?.type === 'sso') {
80+
const activeConnection = this.authUtil.conn
81+
if (this.authUtil.isConnectionValid() && isSsoConnection(activeConnection)) {
8182
// send the token to the language server
8283
const token = await this.authUtil.getBearerToken()
8384
await (force ? this._updateBearerToken(token) : this.updateBearerToken(token))
@@ -118,7 +119,7 @@ export class AmazonQLspAuth {
118119
data: jwt,
119120
metadata: {
120121
sso: {
121-
startUrl: AuthUtil.instance.auth.startUrl,
122+
startUrl: AuthUtil.instance.startUrl,
122123
},
123124
},
124125
encrypted: true,

packages/amazonq/src/lsp/chat/activation.ts

+1-61
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,11 @@ import { Commands, getLogger, globals, undefinedIfEmpty } from 'aws-core-vscode/
1212
import { activate as registerLegacyChatListeners } from '../../app/chat/activation'
1313
import { DefaultAmazonQAppInitContext } from 'aws-core-vscode/amazonq'
1414
import { AuthUtil, getSelectedCustomization } from 'aws-core-vscode/codewhisperer'
15-
import {
16-
DidChangeConfigurationNotification,
17-
updateConfigurationRequestType,
18-
} from '@aws/language-server-runtimes/protocol'
15+
import { pushConfigUpdate } from '../config'
1916

2017
export async function activate(languageClient: LanguageClient, encryptionKey: Buffer, mynahUIPath: string) {
2118
const disposables = globals.context.subscriptions
2219

23-
// Make sure we've sent an auth profile to the language server before even initializing the UI
24-
await pushConfigUpdate(languageClient, {
25-
type: 'profile',
26-
profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn,
27-
})
28-
// We need to push the cached customization on startup explicitly
29-
await pushConfigUpdate(languageClient, {
30-
type: 'customization',
31-
customization: getSelectedCustomization(),
32-
})
33-
3420
const provider = new AmazonQChatViewProvider(mynahUIPath)
3521

3622
disposables.push(
@@ -79,10 +65,6 @@ export async function activate(languageClient: LanguageClient, encryptionKey: Bu
7965

8066
disposables.push(
8167
AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(async () => {
82-
void pushConfigUpdate(languageClient, {
83-
type: 'profile',
84-
profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn,
85-
})
8668
await provider.refreshWebview()
8769
}),
8870
Commands.register('aws.amazonq.updateCustomizations', () => {
@@ -99,45 +81,3 @@ export async function activate(languageClient: LanguageClient, encryptionKey: Bu
9981
})
10082
)
10183
}
102-
103-
/**
104-
* Push a config value to the language server, effectively updating it with the
105-
* latest configuration from the client.
106-
*
107-
* The issue is we need to push certain configs to different places, since there are
108-
* different handlers for specific configs. So this determines the correct place to
109-
* push the given config.
110-
*/
111-
async function pushConfigUpdate(client: LanguageClient, config: QConfigs) {
112-
switch (config.type) {
113-
case 'profile':
114-
await client.sendRequest(updateConfigurationRequestType.method, {
115-
section: 'aws.q',
116-
settings: { profileArn: config.profileArn },
117-
})
118-
break
119-
case 'customization':
120-
client.sendNotification(DidChangeConfigurationNotification.type.method, {
121-
section: 'aws.q',
122-
settings: { customization: config.customization },
123-
})
124-
break
125-
case 'logLevel':
126-
client.sendNotification(DidChangeConfigurationNotification.type.method, {
127-
section: 'aws.logLevel',
128-
})
129-
break
130-
}
131-
}
132-
type ProfileConfig = {
133-
type: 'profile'
134-
profileArn: string | undefined
135-
}
136-
type CustomizationConfig = {
137-
type: 'customization'
138-
customization: string | undefined
139-
}
140-
type LogLevelConfig = {
141-
type: 'logLevel'
142-
}
143-
type QConfigs = ProfileConfig | CustomizationConfig | LogLevelConfig

packages/amazonq/src/lsp/client.ts

+118-106
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
GetConfigurationFromServerParams,
1717
RenameFilesParams,
1818
ResponseMessage,
19-
updateConfigurationRequestType,
2019
WorkspaceFolder,
2120
} from '@aws/language-server-runtimes/protocol'
2221
import { AuthUtil, CodeWhispererSettings, getSelectedCustomization } from 'aws-core-vscode/codewhisperer'
@@ -38,7 +37,7 @@ import {
3837
import { processUtils } from 'aws-core-vscode/shared'
3938
import { activate } from './chat/activation'
4039
import { AmazonQResourcePaths } from './lspInstaller'
41-
import { ConfigSection, isValidConfigSection, toAmazonQLSPLogLevel } from './config'
40+
import { ConfigSection, isValidConfigSection, pushConfigUpdate, toAmazonQLSPLogLevel } from './config'
4241
import { telemetry } from 'aws-core-vscode/telemetry'
4342

4443
const localize = nls.loadMessageBundle()
@@ -160,120 +159,133 @@ export async function startLanguageServer(
160159

161160
const disposable = client.start()
162161
toDispose.push(disposable)
162+
await client.onReady()
163163

164-
const auth = new AmazonQLspAuth(client)
164+
const auth = await initializeAuth(client)
165165

166-
return client.onReady().then(async () => {
167-
await auth.refreshConnection()
166+
await onLanguageServerReady(auth, client, resourcePaths, toDispose)
168167

169-
if (Experiments.instance.get('amazonqLSPInline', false)) {
170-
const inlineManager = new InlineCompletionManager(client)
171-
inlineManager.registerInlineCompletion()
172-
toDispose.push(
173-
inlineManager,
174-
Commands.register({ id: 'aws.amazonq.invokeInlineCompletion', autoconnect: true }, async () => {
175-
await vscode.commands.executeCommand('editor.action.inlineSuggest.trigger')
176-
}),
177-
vscode.workspace.onDidCloseTextDocument(async () => {
178-
await vscode.commands.executeCommand('aws.amazonq.rejectCodeSuggestion')
179-
})
180-
)
181-
}
168+
return client
169+
}
182170

183-
if (Experiments.instance.get('amazonqChatLSP', true)) {
184-
await activate(client, encryptionKey, resourcePaths.ui)
185-
}
171+
async function initializeAuth(client: LanguageClient): Promise<AmazonQLspAuth> {
172+
const auth = new AmazonQLspAuth(client)
173+
await auth.refreshConnection(true)
174+
return auth
175+
}
186176

187-
const refreshInterval = auth.startTokenRefreshInterval(10 * oneSecond)
177+
async function onLanguageServerReady(
178+
auth: AmazonQLspAuth,
179+
client: LanguageClient,
180+
resourcePaths: AmazonQResourcePaths,
181+
toDispose: vscode.Disposable[]
182+
) {
183+
if (Experiments.instance.get('amazonqLSPInline', false)) {
184+
const inlineManager = new InlineCompletionManager(client)
185+
inlineManager.registerInlineCompletion()
186+
toDispose.push(
187+
inlineManager,
188+
Commands.register({ id: 'aws.amazonq.invokeInlineCompletion', autoconnect: true }, async () => {
189+
await vscode.commands.executeCommand('editor.action.inlineSuggest.trigger')
190+
}),
191+
vscode.workspace.onDidCloseTextDocument(async () => {
192+
await vscode.commands.executeCommand('aws.amazonq.rejectCodeSuggestion')
193+
})
194+
)
195+
}
188196

189-
const sendProfileToLsp = async () => {
190-
try {
191-
const result = await client.sendRequest(updateConfigurationRequestType.method, {
192-
section: 'aws.q',
193-
settings: {
194-
profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn,
195-
},
196-
})
197-
client.info(
198-
`Client: Updated Amazon Q Profile ${AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn} to Amazon Q LSP`,
199-
result
200-
)
201-
} catch (err) {
202-
client.error('Error when setting Q Developer Profile to Amazon Q LSP', err)
203-
}
204-
}
197+
if (Experiments.instance.get('amazonqChatLSP', true)) {
198+
await activate(client, encryptionKey, resourcePaths.ui)
199+
}
205200

206-
// send profile to lsp once.
207-
void sendProfileToLsp()
201+
const refreshInterval = auth.startTokenRefreshInterval(10 * oneSecond)
208202

209-
toDispose.push(
210-
AuthUtil.instance.auth.onDidChangeActiveConnection(async () => {
211-
await auth.refreshConnection()
212-
}),
213-
AuthUtil.instance.auth.onDidDeleteConnection(async () => {
214-
client.sendNotification(notificationTypes.deleteBearerToken.method)
215-
}),
216-
AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(sendProfileToLsp),
217-
vscode.commands.registerCommand('aws.amazonq.getWorkspaceId', async () => {
218-
const requestType = new RequestType<GetConfigurationFromServerParams, ResponseMessage, Error>(
219-
'aws/getConfigurationFromServer'
220-
)
221-
const workspaceIdResp = await client.sendRequest(requestType.method, {
222-
section: 'aws.q.workspaceContext',
223-
})
224-
return workspaceIdResp
225-
}),
226-
vscode.workspace.onDidCreateFiles((e) => {
227-
client.sendNotification('workspace/didCreateFiles', {
228-
files: e.files.map((it) => {
229-
return { uri: it.fsPath }
230-
}),
231-
} as CreateFilesParams)
232-
}),
233-
vscode.workspace.onDidDeleteFiles((e) => {
234-
client.sendNotification('workspace/didDeleteFiles', {
235-
files: e.files.map((it) => {
236-
return { uri: it.fsPath }
203+
// We manually push the cached values the first time since event handlers, which should push, may not have been setup yet.
204+
// Execution order is weird and should be fixed in the flare implementation.
205+
// TODO: Revisit if we need this if we setup the event handlers properly
206+
if (AuthUtil.instance.isConnectionValid()) {
207+
await sendProfileToLsp(client)
208+
209+
await pushConfigUpdate(client, {
210+
type: 'customization',
211+
customization: getSelectedCustomization(),
212+
})
213+
}
214+
215+
toDispose.push(
216+
AuthUtil.instance.auth.onDidChangeActiveConnection(async () => {
217+
await auth.refreshConnection()
218+
}),
219+
AuthUtil.instance.auth.onDidDeleteConnection(async () => {
220+
client.sendNotification(notificationTypes.deleteBearerToken.method)
221+
}),
222+
AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(() => sendProfileToLsp(client)),
223+
vscode.commands.registerCommand('aws.amazonq.getWorkspaceId', async () => {
224+
const requestType = new RequestType<GetConfigurationFromServerParams, ResponseMessage, Error>(
225+
'aws/getConfigurationFromServer'
226+
)
227+
const workspaceIdResp = await client.sendRequest(requestType.method, {
228+
section: 'aws.q.workspaceContext',
229+
})
230+
return workspaceIdResp
231+
}),
232+
vscode.workspace.onDidCreateFiles((e) => {
233+
client.sendNotification('workspace/didCreateFiles', {
234+
files: e.files.map((it) => {
235+
return { uri: it.fsPath }
236+
}),
237+
} as CreateFilesParams)
238+
}),
239+
vscode.workspace.onDidDeleteFiles((e) => {
240+
client.sendNotification('workspace/didDeleteFiles', {
241+
files: e.files.map((it) => {
242+
return { uri: it.fsPath }
243+
}),
244+
} as DeleteFilesParams)
245+
}),
246+
vscode.workspace.onDidRenameFiles((e) => {
247+
client.sendNotification('workspace/didRenameFiles', {
248+
files: e.files.map((it) => {
249+
return { oldUri: it.oldUri.fsPath, newUri: it.newUri.fsPath }
250+
}),
251+
} as RenameFilesParams)
252+
}),
253+
vscode.workspace.onDidSaveTextDocument((e) => {
254+
client.sendNotification('workspace/didSaveTextDocument', {
255+
textDocument: {
256+
uri: e.uri.fsPath,
257+
},
258+
} as DidSaveTextDocumentParams)
259+
}),
260+
vscode.workspace.onDidChangeWorkspaceFolders((e) => {
261+
client.sendNotification('workspace/didChangeWorkspaceFolder', {
262+
event: {
263+
added: e.added.map((it) => {
264+
return {
265+
name: it.name,
266+
uri: it.uri.fsPath,
267+
} as WorkspaceFolder
237268
}),
238-
} as DeleteFilesParams)
239-
}),
240-
vscode.workspace.onDidRenameFiles((e) => {
241-
client.sendNotification('workspace/didRenameFiles', {
242-
files: e.files.map((it) => {
243-
return { oldUri: it.oldUri.fsPath, newUri: it.newUri.fsPath }
269+
removed: e.removed.map((it) => {
270+
return {
271+
name: it.name,
272+
uri: it.uri.fsPath,
273+
} as WorkspaceFolder
244274
}),
245-
} as RenameFilesParams)
246-
}),
247-
vscode.workspace.onDidSaveTextDocument((e) => {
248-
client.sendNotification('workspace/didSaveTextDocument', {
249-
textDocument: {
250-
uri: e.uri.fsPath,
251-
},
252-
} as DidSaveTextDocumentParams)
253-
}),
254-
vscode.workspace.onDidChangeWorkspaceFolders((e) => {
255-
client.sendNotification('workspace/didChangeWorkspaceFolder', {
256-
event: {
257-
added: e.added.map((it) => {
258-
return {
259-
name: it.name,
260-
uri: it.uri.fsPath,
261-
} as WorkspaceFolder
262-
}),
263-
removed: e.removed.map((it) => {
264-
return {
265-
name: it.name,
266-
uri: it.uri.fsPath,
267-
} as WorkspaceFolder
268-
}),
269-
},
270-
} as DidChangeWorkspaceFoldersParams)
271-
}),
272-
{ dispose: () => clearInterval(refreshInterval) },
273-
// Set this inside onReady so that it only triggers on subsequent language server starts (not the first)
274-
onServerRestartHandler(client, auth)
275-
)
276-
})
275+
},
276+
} as DidChangeWorkspaceFoldersParams)
277+
}),
278+
{ dispose: () => clearInterval(refreshInterval) },
279+
// Set this inside onReady so that it only triggers on subsequent language server starts (not the first)
280+
onServerRestartHandler(client, auth)
281+
)
282+
283+
async function sendProfileToLsp(client: LanguageClient) {
284+
await pushConfigUpdate(client, {
285+
type: 'profile',
286+
profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn,
287+
})
288+
}
277289
}
278290

279291
/**

0 commit comments

Comments
 (0)