Skip to content

Commit 6ad9824

Browse files
committed
feat(amazonq): paid tier
1 parent fa7a099 commit 6ad9824

File tree

11 files changed

+86
-5
lines changed

11 files changed

+86
-5
lines changed

packages/amazonq/package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,11 @@
392392
"when": "view == aws.amazonq.AmazonQChatView && aws.amazonq.connectedSsoIdc == true",
393393
"group": "1_amazonQ@1"
394394
},
395+
{
396+
"command": "aws.amazonq.manageSubscription",
397+
"when": "(view == aws.amazonq.AmazonQChatView) && aws.codewhisperer.connected",
398+
"group": "2_amazonQ@4"
399+
},
395400
{
396401
"command": "aws.amazonq.signout",
397402
"when": "(view == aws.amazonq.AmazonQChatView) && aws.codewhisperer.connected && !aws.isSageMakerUnifiedStudio",
@@ -673,6 +678,13 @@
673678
"category": "%AWS.amazonq.title%",
674679
"icon": "$(question)"
675680
},
681+
{
682+
"command": "aws.amazonq.manageSubscription",
683+
"title": "%AWS.command.manageSubscription%",
684+
"category": "%AWS.amazonq.title%",
685+
"icon": "$(gear)",
686+
"enablement": "aws.codewhisperer.connected"
687+
},
676688
{
677689
"command": "aws.amazonq.signout",
678690
"title": "%AWS.command.codewhisperer.signout%",

packages/amazonq/src/lsp/auth.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ export class AmazonQLspAuth {
9696
token,
9797
})
9898

99+
// "aws/credentials/token/update"
100+
// https://github.com/aws/language-servers/blob/44d81f0b5754747d77bda60b40cc70950413a737/core/aws-lsp-core/src/credentials/credentialsProvider.ts#L27
99101
await this.client.sendRequest(bearerCredentialsUpdateRequestType.method, request)
100102

101103
this.client.info(`UpdateBearerToken: ${JSON.stringify(request)}`)

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import { window } from 'vscode'
77
import { LanguageClient } from 'vscode-languageclient'
88
import { AmazonQChatViewProvider } from './webviewProvider'
9-
import { registerCommands } from './commands'
9+
import { focusAmazonQPanel, registerCommands } from './commands'
1010
import { registerLanguageServerEventListener, registerMessageListeners } from './messages'
1111
import { Commands, getLogger, globals, undefinedIfEmpty } from 'aws-core-vscode/shared'
1212
import { activate as registerLegacyChatListeners } from '../../app/chat/activation'
@@ -73,6 +73,14 @@ export async function activate(languageClient: LanguageClient, encryptionKey: Bu
7373
customization: undefinedIfEmpty(getSelectedCustomization().arn),
7474
})
7575
}),
76+
Commands.register('aws.amazonq.manageSubscription', () => {
77+
focusAmazonQPanel().catch((e) => languageClient.error(`[VSCode Client] focusAmazonQPanel() failed`))
78+
79+
languageClient.sendRequest('workspace/executeCommand', {
80+
command: 'aws/chat/manageSubscription',
81+
// arguments: [],
82+
})
83+
}),
7684
globals.logOutputChannel.onDidChangeLogLevel((logLevel) => {
7785
getLogger('amazonqLsp').info(`Local log level changed to ${logLevel}, notifying LSP`)
7886
void pushConfigUpdate(languageClient, {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ function registerGenericCommand(commandName: string, genericCommand: string, pro
125125
*
126126
* Instead, we just create our own as a temporary solution
127127
*/
128-
async function focusAmazonQPanel() {
128+
export async function focusAmazonQPanel() {
129129
await Commands.tryExecute('aws.amazonq.AmazonQChatView.focus')
130130
await Commands.tryExecute('aws.amazonq.AmazonCommonAuth.focus')
131131
}

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import * as jose from 'jose'
6161
import { AmazonQChatViewProvider } from './webviewProvider'
6262
import { AuthUtil, ReferenceLogViewProvider } from 'aws-core-vscode/codewhisperer'
6363
import { amazonQDiffScheme, AmazonQPromptSettings, messages, openUrl } from 'aws-core-vscode/shared'
64+
import { credentialsValidation } from 'aws-core-vscode/auth'
6465
import {
6566
DefaultAmazonQAppInitContext,
6667
messageDispatcher,
@@ -70,6 +71,7 @@ import {
7071
} from 'aws-core-vscode/amazonq'
7172
import { telemetry, TelemetryBase } from 'aws-core-vscode/telemetry'
7273
import { isValidResponseError } from './error'
74+
import { focusAmazonQPanel } from './commands'
7375

7476
export function registerLanguageServerEventListener(languageClient: LanguageClient, provider: AmazonQChatViewProvider) {
7577
languageClient.info(
@@ -326,13 +328,33 @@ export function registerMessageListeners(
326328
}
327329
break
328330
case buttonClickRequestType.method: {
331+
if (message.params.buttonId === 'paidtier-upgrade-q') {
332+
focusAmazonQPanel().catch((e) => languageClient.error(`[VSCode Client] focusAmazonQPanel() failed`))
333+
334+
const accountId = await vscode.window.showInputBox({
335+
title: 'Upgrade Amazon Q',
336+
prompt: 'Enter your 12-digit AWS account ID',
337+
placeHolder: '111111111111',
338+
validateInput: credentialsValidation.validateAwsAccount,
339+
})
340+
341+
if (accountId) {
342+
languageClient.sendRequest('workspace/executeCommand', {
343+
command: 'aws/chat/manageSubscription',
344+
arguments: [accountId],
345+
})
346+
} else {
347+
languageClient.error('[VSCode Client] user canceled or did not input AWS account id')
348+
}
349+
}
350+
329351
const buttonResult = await languageClient.sendRequest<ButtonClickResult>(
330352
buttonClickRequestType.method,
331353
message.params
332354
)
333355
if (!buttonResult.success) {
334356
languageClient.error(
335-
`[VSCode Client] Failed to execute action associated with button with reason: ${buttonResult.failureReason}`
357+
`[VSCode Client] Failed to execute button action: ${buttonResult.failureReason}`
336358
)
337359
}
338360
break
@@ -431,6 +453,8 @@ export function registerMessageListeners(
431453
languageClient.onRequest<ShowDocumentParams, ShowDocumentResult>(
432454
ShowDocumentRequest.method,
433455
async (params: ShowDocumentParams): Promise<ShowDocumentParams | ResponseError<ShowDocumentResult>> => {
456+
focusAmazonQPanel().catch((e) => languageClient.error(`[VSCode Client] focusAmazonQPanel() failed`))
457+
434458
try {
435459
const uri = vscode.Uri.parse(params.uri)
436460

packages/core/package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
"AWS.command.codecatalyst.login": "Connect to CodeCatalyst",
138138
"AWS.command.codecatalyst.logout": "Sign out of CodeCatalyst",
139139
"AWS.command.codecatalyst.signout": "Sign Out",
140+
"AWS.command.manageSubscription": "Manage Subscription",
140141
"AWS.command.amazonq.explainCode": "Explain",
141142
"AWS.command.amazonq.refactorCode": "Refactor",
142143
"AWS.command.amazonq.fixCode": "Fix",

packages/core/src/auth/credentials/validation.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ async function validateProfileName(profileName: SectionName) {
127127
}
128128
}
129129

130+
export function validateAwsAccount(s: string): string | undefined {
131+
// AWS account IDs are exactly 12 digits
132+
if (!/^\d{12}$/.test(s)) {
133+
return 'Enter a valid 12-digit AWS account ID'
134+
}
135+
return undefined
136+
}
137+
130138
// All shared credentials keys
131139
const sharedCredentialsKeysSet = new Set(Object.values(SharedCredentialsKeys))
132140

packages/core/src/auth/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ export { Auth } from './auth'
2424
export { CredentialsStore } from './credentials/store'
2525
export { LoginManager } from './deprecated/loginManager'
2626
export * as AuthUtils from './utils'
27+
export * as credentialsValidation from './credentials/validation'

packages/core/src/codewhisperer/ui/codeWhispererNodes.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,19 @@ export function createGettingStarted(): DataQuickPickItem<'gettingStarted'> {
175175
} as DataQuickPickItem<'gettingStarted'>
176176
}
177177

178+
export function createManageSubscription(): DataQuickPickItem<'manageSubscription'> {
179+
const label = localize('AWS.command.manageSubscription', 'Manage Subscription')
180+
// const kind = AuthUtil.instance.isBuilderIdInUse() ? 'AWS Builder ID' : 'IAM Identity Center'
181+
182+
return {
183+
data: 'manageSubscription',
184+
label: label,
185+
iconPath: getIcon('vscode-link-external'),
186+
// TODO
187+
onClick: () => Commands.tryExecute('aws.amazonq.manageSubscription'),
188+
} as DataQuickPickItem<'manageSubscription'>
189+
}
190+
178191
export function createSignout(): DataQuickPickItem<'signout'> {
179192
const label = localize('AWS.codewhisperer.signoutNode.label', 'Sign Out')
180193
const icon = getIcon('vscode-export')

packages/core/src/codewhisperer/ui/statusBarMenu.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
createSelectCustomization,
1212
createReconnect,
1313
createGettingStarted,
14+
createManageSubscription,
1415
createSignout,
1516
createSeparator,
1617
createSettingsNode,
@@ -106,7 +107,7 @@ export function getQuickPickItems(): DataQuickPickItem<string>[] {
106107
createSettingsNode(),
107108
...(isUsingEnterpriseSso && regionProfile ? [createSelectRegionProfileNode(regionProfile)] : []),
108109
...(AuthUtil.instance.isConnected() && !hasVendedIamCredentials() && !hasVendedCredentialsFromMetadata()
109-
? [createSignout()]
110+
? [createManageSubscription(), createSignout()]
110111
: []),
111112
]
112113

0 commit comments

Comments
 (0)