diff --git a/packages/amazonq/.changes/next-release/Bug Fix-6f1c2d3e-4b5a-4c6d-9e8f-0a1b2c3d4e5f.json b/packages/amazonq/.changes/next-release/Bug Fix-6f1c2d3e-4b5a-4c6d-9e8f-0a1b2c3d4e5f.json new file mode 100644 index 0000000000..6ebdf547e1 --- /dev/null +++ b/packages/amazonq/.changes/next-release/Bug Fix-6f1c2d3e-4b5a-4c6d-9e8f-0a1b2c3d4e5f.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Amazon Q: In browser-only VS Code (vscode.dev, github.dev), a clear message now guides you to desktop VS Code or a remote environment where Amazon Q is fully supported." +} diff --git a/packages/amazonq/src/extension.ts b/packages/amazonq/src/extension.ts index 9b83695205..496d5dc421 100644 --- a/packages/amazonq/src/extension.ts +++ b/packages/amazonq/src/extension.ts @@ -127,7 +127,11 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is // This contains every lsp agnostic things (auth, security scan, code scan) await activateCodeWhisperer(extContext as ExtContext) - if (!isAmazonLinux2() || hasGlibcPatch()) { + if (isWeb) { + // The language server runs as a child process, which the browser-only extension host cannot spawn. + // Skip it instead of surfacing a confusing "Failed to launch Amazon Q language server" error. + getLogger('amazonqLsp').info('skipping language server activation: not supported in the web extension host') + } else if (!isAmazonLinux2() || hasGlibcPatch()) { // Activate Amazon Q LSP for everyone unless they're using AL2 without the glibc patch await activateAmazonqLsp(context) } @@ -169,7 +173,9 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is // reload webviews await vscode.commands.executeCommand('workbench.action.webview.reloadWebviewAction') - if (AuthUtils.ExtensionUse.instance.isFirstUse()) { + // The chat panel is not available in the web extension host (see `!aws.isWebExtHost` in package.json), + // so there is nothing to focus there. + if (!isWeb && AuthUtils.ExtensionUse.instance.isFirstUse()) { // Give time for the extension to finish initializing. globals.clock.setTimeout(async () => { CommonAuthWebview.authSource = ExtStartUpSources.firstStartUp diff --git a/packages/amazonq/src/extensionWeb.ts b/packages/amazonq/src/extensionWeb.ts index 3091cda2da..e55b089b43 100644 --- a/packages/amazonq/src/extensionWeb.ts +++ b/packages/amazonq/src/extensionWeb.ts @@ -5,11 +5,16 @@ import type { ExtensionContext } from 'vscode' import { activateWebShared } from 'aws-core-vscode/webShared' +import { showWebModeUnsupportedMessageOnce } from 'aws-core-vscode/amazonq' import { activateAmazonQCommon, deactivateCommon } from './extension' export async function activate(context: ExtensionContext) { await activateWebShared(context) await activateAmazonQCommon(context, true) + // Amazon Q needs a Node.js extension host (desktop VS Code, or a browser editor connected to a + // remote environment). In the browser-only extension host the language server cannot start, so + // tell the user up front instead of letting them click "Sign in" and see nothing happen. + showWebModeUnsupportedMessageOnce() } export async function deactivate() { diff --git a/packages/core/src/amazonq/index.ts b/packages/core/src/amazonq/index.ts index 2d4b830b5e..327bacd2c1 100644 --- a/packages/core/src/amazonq/index.ts +++ b/packages/core/src/amazonq/index.ts @@ -9,6 +9,7 @@ export { MessagePublisher } from './messages/messagePublisher' export { MessageListener } from './messages/messageListener' export { AuthController } from './auth/controller' export { showAmazonQWalkthroughOnce } from './onboardingPage/walkthrough' +export { showWebModeUnsupportedMessage, showWebModeUnsupportedMessageOnce, webModeUnsupportedMessage } from './webMode' export { focusAmazonQChatWalkthrough, openAmazonQWalkthrough, diff --git a/packages/core/src/amazonq/webMode.ts b/packages/core/src/amazonq/webMode.ts new file mode 100644 index 0000000000..064c9c26de --- /dev/null +++ b/packages/core/src/amazonq/webMode.ts @@ -0,0 +1,34 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { showMessage } from '../shared/utilities/messages' +import { once } from '../shared/utilities/functionUtils' + +/** + * Amazon Q depends on the Amazon Q language server, which runs as a Node.js child process. + * The browser-only VS Code extension host (for example vscode.dev or github.dev opened without + * a remote/backend environment) cannot spawn processes, so Amazon Q features cannot work there. + * + * Users end up with an extension that appears installed but does nothing when they click "Sign in". + * This message tells them why, and what to do instead. + */ +export const webModeUnsupportedMessage = + 'Amazon Q is not available in the browser-only version of VS Code (for example vscode.dev or github.dev without a remote environment). ' + + 'Use VS Code on your desktop, or connect the browser editor to a remote environment such as a codespace or a remote host, to use Amazon Q.' + +/** + * Shows a non-modal notification explaining that Amazon Q does not work in the browser-only extension host. + */ +export function showWebModeUnsupportedMessage(): Thenable { + return showMessage('info', webModeUnsupportedMessage, [], {}, { id: 'amazonqWebModeUnsupported' }) +} + +/** + * Same as {@link showWebModeUnsupportedMessage}, but only shows the notification once per session. + * Intended for extension activation so the notification is not repeated. + */ +export const showWebModeUnsupportedMessageOnce = once(() => { + void showWebModeUnsupportedMessage() +}) diff --git a/packages/core/src/codewhisperer/ui/codeWhispererNodes.ts b/packages/core/src/codewhisperer/ui/codeWhispererNodes.ts index c1934ec6a7..92618e1040 100644 --- a/packages/core/src/codewhisperer/ui/codeWhispererNodes.ts +++ b/packages/core/src/codewhisperer/ui/codeWhispererNodes.ts @@ -29,6 +29,7 @@ import { submitFeedback } from '../../feedback/vue/submitFeedback' import { focusAmazonQPanel } from '../../codewhispererChat/commands/registerCommands' import { isWeb } from '../../shared/extensionGlobals' import { getLogger } from '../../shared/logger/logger' +import { showWebModeUnsupportedMessage } from '../../amazonq/webMode' export function createAutoSuggestions(running: boolean): DataQuickPickItem<'autoSuggestions'> { const labelResume = localize('AWS.codewhisperer.resumeCodeWhispererNode.label', 'Resume Auto-Suggestions') @@ -249,9 +250,10 @@ export function createSignIn(): DataQuickPickItem<'signIn'> { }) } if (isWeb()) { - // TODO: nkomonen, call a Command instead + // Amazon Q cannot run in the browser-only extension host (no language server), so signing in + // would not lead anywhere. Explain why instead of starting an auth flow that appears to do nothing. onClick = () => { - void AuthUtil.instance.connectToAwsBuilderId() + void showWebModeUnsupportedMessage() } } diff --git a/packages/core/src/test/amazonq/webMode.test.ts b/packages/core/src/test/amazonq/webMode.test.ts new file mode 100644 index 0000000000..9838595c0b --- /dev/null +++ b/packages/core/src/test/amazonq/webMode.test.ts @@ -0,0 +1,56 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import sinon from 'sinon' +import globals from '../../shared/extensionGlobals' +import { showWebModeUnsupportedMessage, webModeUnsupportedMessage } from '../../amazonq/webMode' +import { createSignIn } from '../../codewhisperer/ui/codeWhispererNodes' +import { getTestWindow } from '../shared/vscode/window' + +/** `waitForMessage` treats strings as regex patterns, so match on a prefix without special characters. */ +const webModeMessagePattern = /^Amazon Q is not available in the browser-only version of VS Code/ + +describe('amazonq webMode', function () { + let sandbox: sinon.SinonSandbox + + beforeEach(function () { + sandbox = sinon.createSandbox() + }) + + afterEach(function () { + sandbox.restore() + }) + + describe('showWebModeUnsupportedMessage', function () { + it('shows an informational notification', async function () { + const shown = getTestWindow().waitForMessage(webModeMessagePattern) + void showWebModeUnsupportedMessage() + const message = await shown + message.assertInfo(webModeUnsupportedMessage) + }) + }) + + describe('createSignIn', function () { + it('explains that Amazon Q is unavailable when running in the web extension host', async function () { + sandbox.stub(globals, 'isWeb').value(true) + + const shown = getTestWindow().waitForMessage(webModeMessagePattern) + createSignIn().onClick?.() + const message = await shown + message.assertInfo(webModeUnsupportedMessage) + }) + + it('does not show the web notice outside of the web extension host', function () { + sandbox.stub(globals, 'isWeb').value(false) + + createSignIn().onClick?.() + assert.strictEqual( + getTestWindow().shownMessages.some((m) => m.message === webModeUnsupportedMessage), + false + ) + }) + }) +})