Skip to content

Commit 18d0f39

Browse files
committed
Rebase
2 parents fcfb132 + edb3802 commit 18d0f39

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+591
-272
lines changed

package-lock.json

Lines changed: 13 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "Users might be bound to a customization which they dont have access with the selected profile and it causes service throwing 403 when using inline suggestion and chat features"
4+
}

packages/amazonq/src/extension.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,10 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is
119119
extensionContext: context,
120120
}
121121

122+
await activateAmazonqLsp(context)
123+
122124
// This contains every lsp agnostic things (auth, security scan, code scan)
123125
await activateCodeWhisperer(extContext as ExtContext)
124-
await activateAmazonqLsp(context)
125126

126127
if (!Experiments.instance.get('amazonqLSPInline', false)) {
127128
await activateInlineCompletion()

packages/amazonq/src/extensionNode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ async function activateAmazonQNode(context: vscode.ExtensionContext) {
8484
authEnabledConnections: (await AuthUtil.instance.getAuthFormIds()).join(','),
8585
})
8686

87-
void activateNotifications(context, getAuthState)
87+
// void activateNotifications(context, getAuthState)
8888
}
8989

9090
async function getAuthState(): Promise<Omit<AuthUserState, 'source'>> {

packages/amazonq/src/lsp/activation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import vscode from 'vscode'
77
import { clientId, encryptionKey, startLanguageServer } from './client'
88
import { AmazonQLspInstaller } from './lspInstaller'
9-
import { lspSetupStage, ToolkitError } from 'aws-core-vscode/shared'
9+
import { lspSetupStage, messages, ToolkitError } from 'aws-core-vscode/shared'
1010
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
1111
import { auth2 } from 'aws-core-vscode/auth'
1212

@@ -20,6 +20,6 @@ export async function activate(ctx: vscode.ExtensionContext) {
2020
await AuthUtil.instance.restore()
2121
} catch (err) {
2222
const e = err as ToolkitError
23-
void vscode.window.showInformationMessage(`Unable to launch amazonq language server: ${e.message}`)
23+
void messages.showViewLogsMessage(`Failed to launch Amazon Q language server: ${e.message}`)
2424
}
2525
}

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

Lines changed: 128 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
COPY_TO_CLIPBOARD,
1212
AuthFollowUpType,
1313
DISCLAIMER_ACKNOWLEDGED,
14+
UiMessageResultParams,
1415
} from '@aws/chat-client-ui-types'
1516
import {
1617
ChatResult,
@@ -21,30 +22,42 @@ import {
2122
QuickActionResult,
2223
QuickActionParams,
2324
insertToCursorPositionNotificationType,
25+
ErrorCodes,
26+
ResponseError,
27+
openTabRequestType,
28+
getSerializedChatRequestType,
29+
listConversationsRequestType,
30+
conversationClickRequestType,
31+
ShowSaveFileDialogRequestType,
32+
ShowSaveFileDialogParams,
33+
LSPErrorCodes,
34+
tabBarActionRequestType,
2435
} from '@aws/language-server-runtimes/protocol'
2536
import { v4 as uuidv4 } from 'uuid'
2637
import * as vscode from 'vscode'
27-
import { Disposable, LanguageClient, Position, State, TextDocumentIdentifier } from 'vscode-languageclient'
38+
import { Disposable, LanguageClient, Position, TextDocumentIdentifier } from 'vscode-languageclient'
2839
import * as jose from 'jose'
2940
import { AmazonQChatViewProvider } from './webviewProvider'
3041
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
3142
import { AmazonQPromptSettings, messages } from 'aws-core-vscode/shared'
3243

3344
export function registerLanguageServerEventListener(languageClient: LanguageClient, provider: AmazonQChatViewProvider) {
34-
languageClient.onDidChangeState(({ oldState, newState }) => {
35-
if (oldState === State.Starting && newState === State.Running) {
36-
languageClient.info(
37-
'Language client received initializeResult from server:',
38-
JSON.stringify(languageClient.initializeResult)
39-
)
45+
languageClient.info(
46+
'Language client received initializeResult from server:',
47+
JSON.stringify(languageClient.initializeResult)
48+
)
4049

41-
const chatOptions = languageClient.initializeResult?.awsServerCapabilities?.chatOptions
50+
const chatOptions = languageClient.initializeResult?.awsServerCapabilities?.chatOptions
4251

43-
void provider.webview?.postMessage({
44-
command: CHAT_OPTIONS,
45-
params: chatOptions,
46-
})
47-
}
52+
// Enable the history/export feature flags
53+
chatOptions.history = true
54+
chatOptions.export = true
55+
56+
provider.onDidResolveWebview(() => {
57+
void provider.webview?.postMessage({
58+
command: CHAT_OPTIONS,
59+
params: chatOptions,
60+
})
4861
})
4962

5063
languageClient.onTelemetry((e) => {
@@ -60,6 +73,7 @@ export function registerMessageListeners(
6073
provider.webview?.onDidReceiveMessage(async (message) => {
6174
languageClient.info(`[VSCode Client] Received ${JSON.stringify(message)} from chat`)
6275

76+
const webview = provider.webview
6377
switch (message.command) {
6478
case COPY_TO_CLIPBOARD:
6579
languageClient.info('[VSCode Client] Copy to clipboard event received')
@@ -172,6 +186,11 @@ export function registerMessageListeners(
172186
)
173187
break
174188
}
189+
case listConversationsRequestType.method:
190+
case conversationClickRequestType.method:
191+
case tabBarActionRequestType.method:
192+
await resolveChatResponse(message.command, message.params, languageClient, webview)
193+
break
175194
case followUpClickNotificationType.method:
176195
if (!isValidAuthFollowUpType(message.params.followUp.type)) {
177196
languageClient.sendNotification(followUpClickNotificationType.method, message.params)
@@ -184,6 +203,89 @@ export function registerMessageListeners(
184203
break
185204
}
186205
}, undefined)
206+
207+
const registerHandlerWithResponseRouter = (command: string) => {
208+
const handler = async (params: any, _: any) => {
209+
const mapErrorType = (type: string | undefined): number => {
210+
switch (type) {
211+
case 'InvalidRequest':
212+
return ErrorCodes.InvalidRequest
213+
case 'InternalError':
214+
return ErrorCodes.InternalError
215+
case 'UnknownError':
216+
default:
217+
return ErrorCodes.UnknownErrorCode
218+
}
219+
}
220+
const requestId = uuidv4()
221+
222+
void provider.webview?.postMessage({
223+
requestId: requestId,
224+
command: command,
225+
params: params,
226+
})
227+
const responsePromise = new Promise<UiMessageResultParams | undefined>((resolve, reject) => {
228+
const timeout = setTimeout(() => {
229+
disposable?.dispose()
230+
reject(new Error('Request timed out'))
231+
}, 30000)
232+
233+
const disposable = provider.webview?.onDidReceiveMessage((message: any) => {
234+
if (message.requestId === requestId) {
235+
clearTimeout(timeout)
236+
disposable?.dispose()
237+
resolve(message.params)
238+
}
239+
})
240+
})
241+
242+
const result = await responsePromise
243+
244+
if (result?.success) {
245+
return result.result
246+
} else {
247+
return new ResponseError(
248+
mapErrorType(result?.error.type),
249+
result?.error.message ?? 'No response from client'
250+
)
251+
}
252+
}
253+
254+
languageClient.onRequest(command, handler)
255+
}
256+
257+
registerHandlerWithResponseRouter(openTabRequestType.method)
258+
registerHandlerWithResponseRouter(getSerializedChatRequestType.method)
259+
260+
languageClient.onRequest(ShowSaveFileDialogRequestType.method, async (params: ShowSaveFileDialogParams) => {
261+
const filters: Record<string, string[]> = {}
262+
const formatMappings = [
263+
{ format: 'markdown', key: 'Markdown', extensions: ['md'] },
264+
{ format: 'html', key: 'HTML', extensions: ['html'] },
265+
]
266+
267+
for (const format of params.supportedFormats ?? []) {
268+
const mapping = formatMappings.find((m) => m.format === format)
269+
if (mapping) {
270+
filters[mapping.key] = mapping.extensions
271+
}
272+
}
273+
274+
const saveAtUri = params.defaultUri ? vscode.Uri.parse(params.defaultUri) : vscode.Uri.file('export-chat.md')
275+
const targetUri = await vscode.window.showSaveDialog({
276+
filters,
277+
defaultUri: saveAtUri,
278+
title: 'Export',
279+
})
280+
281+
if (!targetUri) {
282+
return new ResponseError(LSPErrorCodes.RequestFailed, 'Export failed')
283+
}
284+
285+
return {
286+
targetUri: targetUri.toString(),
287+
}
288+
})
187289
}
188290

189291
function isServerEvent(command: string) {
@@ -258,3 +360,16 @@ async function handleCompleteResult<T>(
258360
})
259361
disposable.dispose()
260362
}
363+
364+
async function resolveChatResponse(
365+
requestMethod: string,
366+
params: any,
367+
languageClient: LanguageClient,
368+
webview: vscode.Webview | undefined
369+
) {
370+
const result = await languageClient.sendRequest(requestMethod, params)
371+
void webview?.postMessage({
372+
command: requestMethod,
373+
params: result,
374+
})
375+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export class AmazonQChatViewProvider implements WebviewViewProvider {
5050
) {
5151
this.webview = webviewView.webview
5252

53-
const lspDir = Uri.parse(LanguageServerResolver.defaultDir)
53+
const lspDir = Uri.parse(LanguageServerResolver.defaultDir())
5454
webviewView.webview.options = {
5555
enableScripts: true,
5656
enableCommandUris: true,
@@ -64,7 +64,7 @@ export class AmazonQChatViewProvider implements WebviewViewProvider {
6464
}
6565

6666
private async getWebviewContent(mynahUIPath: string) {
67-
const disclaimerAcknowledged = AmazonQPromptSettings.instance.isPromptEnabled('amazonQChatDisclaimer')
67+
const disclaimerAcknowledged = !AmazonQPromptSettings.instance.isPromptEnabled('amazonQChatDisclaimer')
6868
return `
6969
<!DOCTYPE html>
7070
<html lang="en">

packages/amazonq/src/lsp/client.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@ import {
3131
Commands,
3232
openUrl,
3333
getLogger,
34+
validateNodeExe,
3435
} from 'aws-core-vscode/shared'
3536
import { activate } from './chat/activation'
3637
import { AmazonQResourcePaths } from './lspInstaller'
3738
import { auth2 } from 'aws-core-vscode/auth'
3839

3940
const localize = nls.loadMessageBundle()
41+
const logger = getLogger('amazonqLsp.lspClient')
4042

4143
export const clientId = 'amazonq'
4244
export const clientName = oidcClientName()
@@ -50,22 +52,25 @@ export async function startLanguageServer(
5052

5153
const serverModule = resourcePaths.lsp
5254

55+
const argv = [
56+
'--nolazy',
57+
'--preserve-symlinks',
58+
'--stdio',
59+
'--pre-init-encryption',
60+
'--set-credentials-encryption-key',
61+
]
5362
const serverOptions = createServerOptions({
5463
encryptionKey,
5564
executable: resourcePaths.node,
5665
serverModule,
57-
execArgv: [
58-
'--nolazy',
59-
'--preserve-symlinks',
60-
'--stdio',
61-
'--pre-init-encryption',
62-
'--set-credentials-encryption-key',
63-
],
66+
execArgv: argv,
6467
})
6568

6669
const documentSelector = [{ scheme: 'file', language: '*' }]
6770
const traceServerEnabled = Settings.instance.isSet(`${clientId}.trace.server`)
6871

72+
await validateNodeExe(resourcePaths.node, resourcePaths.lsp, argv, logger)
73+
6974
// Options to control the language client
7075
const clientOptions: LanguageClientOptions = {
7176
// Register the server for json documents

0 commit comments

Comments
 (0)