diff --git a/plugins/amazonq/chat/jetbrains-community/resources/META-INF/plugin-chat.xml b/plugins/amazonq/chat/jetbrains-community/resources/META-INF/plugin-chat.xml index 5d4e24f84be..3a2a8813ff9 100644 --- a/plugins/amazonq/chat/jetbrains-community/resources/META-INF/plugin-chat.xml +++ b/plugins/amazonq/chat/jetbrains-community/resources/META-INF/plugin-chat.xml @@ -33,6 +33,7 @@ + diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/GetAmazonQLogsAction.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/GetAmazonQLogsAction.kt new file mode 100644 index 00000000000..0222bba8798 --- /dev/null +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/GetAmazonQLogsAction.kt @@ -0,0 +1,68 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.amazonq + +import com.intellij.icons.AllIcons +import com.intellij.ide.actions.RevealFileAction +import com.intellij.ide.logsUploader.LogPacker +import com.intellij.openapi.actionSystem.ActionUpdateThread +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAwareAction +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.Messages +import com.intellij.openapi.util.IconLoader +import com.intellij.ui.ColorUtil +import com.intellij.ui.JBColor +import com.intellij.util.IconUtil +import com.intellij.util.ui.UIUtil +import kotlinx.coroutines.runBlocking +import software.aws.toolkits.jetbrains.utils.notifyInfo +import software.aws.toolkits.jetbrains.utils.runUnderProgressIfNeeded +import software.aws.toolkits.resources.AmazonQBundle.message +import software.aws.toolkits.resources.AwsCoreBundle + +class GetAmazonQLogsAction : DumbAwareAction(message("amazonq.getLogs.tooltip.text")) { + private val baseIcon = IconLoader.getIcon("/icons/file.svg", GetAmazonQLogsAction::class.java) + + private val lightIcon by lazy { + IconUtil.colorize(baseIcon, ColorUtil.brighter(UIUtil.getLabelForeground(), 2)) + } + + override fun update(e: AnActionEvent) { + e.presentation.icon = if (!JBColor.isBright()) { + baseIcon + } else { + lightIcon + } + } + + override fun getActionUpdateThread() = ActionUpdateThread.BGT + override fun actionPerformed(e: AnActionEvent) { + val project = e.project ?: return + showLogCollectionWarningGetLogs(project) + } + + companion object { + fun showLogCollectionWarningGetLogs(project: Project) { + if (Messages.showOkCancelDialog( + message("amazonq.logs.warning"), + message("amazonq.getLogs"), + AwsCoreBundle.message("general.ok"), + AwsCoreBundle.message("general.cancel"), + AllIcons.General.Warning + ) == 0 + ) { + runUnderProgressIfNeeded(project, message("amazonq.getLogs"), cancelable = true) { + runBlocking { + try { + RevealFileAction.openFile(LogPacker.packLogs(project)) + } catch (_: Exception) { + notifyInfo(message("amazonq.getLogs"), message("amazonq.logs.error"), project) + } + } + } + } + } + } +} diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt index 6241eefedf3..8008c552278 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt @@ -33,6 +33,7 @@ import software.aws.toolkits.core.utils.error import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.core.utils.info import software.aws.toolkits.core.utils.warn +import software.aws.toolkits.jetbrains.services.amazonq.GetAmazonQLogsAction import software.aws.toolkits.jetbrains.services.amazonq.apps.AppConnection import software.aws.toolkits.jetbrains.services.amazonq.commands.MessageSerializer import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQChatServer @@ -419,33 +420,40 @@ class BrowserConnector( } CHAT_TAB_BAR_ACTIONS -> { - handleChat(AmazonQChatServer.tabBarActions, node) { params, invoke -> - invoke() - .whenComplete { actions, error -> - try { - if (error != null) { - throw error - } - - browser.postChat( - FlareUiMessage( - command = CHAT_TAB_BAR_ACTIONS, - params = actions + val action = node.params.get("action") + if (action.textValue() == "show_logs") { + runInEdt { + GetAmazonQLogsAction.showLogCollectionWarningGetLogs(project) + } + } else { + handleChat(AmazonQChatServer.tabBarActions, node) { params, invoke -> + invoke() + .whenComplete { actions, error -> + try { + if (error != null) { + throw error + } + + browser.postChat( + FlareUiMessage( + command = CHAT_TAB_BAR_ACTIONS, + params = actions + ) ) - ) - } catch (e: Exception) { - val cause = if (e is CompletionException) e.cause else e - - // dont post error to UI if user cancels export - if (cause is ResponseErrorException && cause.responseError.code == ResponseErrorCode.RequestCancelled.getValue()) { - return@whenComplete - } - LOG.error { "Failed to perform chat tab bar action $e" } - params.tabId?.let { - browser.postChat(chatCommunicationManager.getErrorUiMessage(it, e, null)) + } catch (e: Exception) { + val cause = if (e is CompletionException) e.cause else e + + // dont post error to UI if user cancels export + if (cause is ResponseErrorException && cause.responseError.code == ResponseErrorCode.RequestCancelled.getValue()) { + return@whenComplete + } + LOG.error { "Failed to perform chat tab bar action $e" } + params.tabId?.let { + browser.postChat(chatCommunicationManager.getErrorUiMessage(it, e, null)) + } } } - } + } } } diff --git a/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties b/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties index e780cdb6154..761a500ca06 100644 --- a/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties +++ b/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties @@ -6,6 +6,10 @@ amazonqInlineChat.popup.editCode = Edit Code amazonqInlineChat.popup.generating = Generating... amazonqInlineChat.popup.reject=Reject \u238B amazonqInlineChat.popup.title=Enter Instructions for Q +amazonq.getLogs=Get Amazon Q logs +amazonq.getLogs.tooltip.text=Show Logs +amazonq.logs.error=Cannot retrieve logs. Please try Help-> Collect Logs and Diagnostic data +amazonq.logs.warning=Log files may contain sensitive information such as account IDs, resource names, and other data. Please be careful when sharing these logs. amazonq.refresh.panel=Refresh Chat Session amazonq.title=Amazon Q amazonq.workspace.settings.open.prompt=Workspace index is now enabled. You can disable it from Amazon Q settings. diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AwsServerCapabilitiesProvider.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AwsServerCapabilitiesProvider.kt index bae6b41c363..53fbb136521 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AwsServerCapabilitiesProvider.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AwsServerCapabilitiesProvider.kt @@ -37,6 +37,7 @@ class AwsServerCapabilitiesProvider { mcpServers = true, // Seems like this is the only way to bounce reroute back to the LSP? reroute = true, + showLogs = true ) } } @@ -51,6 +52,7 @@ data class ChatOptions( val export: Boolean, val mcpServers: Boolean, val reroute: Boolean, + val showLogs: Boolean, ) data class QuickActions( diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/ExtendedClientMetadata.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/ExtendedClientMetadata.kt index 8168645d829..e783d0e3147 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/ExtendedClientMetadata.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/ExtendedClientMetadata.kt @@ -32,6 +32,7 @@ data class DeveloperProfiles( data class WindowSettings( val showSaveFileDialog: Boolean, + val showLogs: Boolean, ) data class ClientInfoMetadata( @@ -73,7 +74,8 @@ fun createExtendedClientMetadata(project: Project): ExtendedClientMetadata { workspaceFilePath = project.workspaceFile?.path, ), window = WindowSettings( - showSaveFileDialog = true + showSaveFileDialog = true, + showLogs = true, ) ), contextConfiguration = ContextConfiguration( diff --git a/plugins/amazonq/src/main/resources/icons/file.svg b/plugins/amazonq/src/main/resources/icons/file.svg new file mode 100644 index 00000000000..2f57827e2e4 --- /dev/null +++ b/plugins/amazonq/src/main/resources/icons/file.svg @@ -0,0 +1,7 @@ + + + + Layer 1 + + + \ No newline at end of file