Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/aws-lsp-codewhisperer-runtimes/src/agent-standalone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
CodeWhispererServer,
QAgenticChatServerProxy,
QConfigurationServerTokenProxy,
TransformConfigurationServerTokenProxy,
AtxNetTransformServerTokenProxy,
QLocalProjectContextServerProxy,
QNetTransformServerTokenProxy,
WorkspaceContextServerTokenProxy,
Expand All @@ -28,6 +30,8 @@ const props = {
CodeWhispererServer,
CodeWhispererSecurityScanServerTokenProxy,
QConfigurationServerTokenProxy,
TransformConfigurationServerTokenProxy,
AtxNetTransformServerTokenProxy,
QNetTransformServerTokenProxy,
QAgenticChatServerProxy,
IdentityServer.create,
Expand Down
4 changes: 4 additions & 0 deletions app/aws-lsp-codewhisperer-runtimes/src/token-standalone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
CodeWhispererServerTokenProxy,
QChatServerTokenProxy,
QConfigurationServerTokenProxy,
TransformConfigurationServerTokenProxy,
AtxNetTransformServerTokenProxy,
QNetTransformServerTokenProxy,
QLocalProjectContextServerProxy,
WorkspaceContextServerTokenProxy,
Expand All @@ -20,6 +22,8 @@ const props = createTokenRuntimeProps(VERSION, [
CodeWhispererServerTokenProxy,
CodeWhispererSecurityScanServerTokenProxy,
QConfigurationServerTokenProxy,
TransformConfigurationServerTokenProxy,
AtxNetTransformServerTokenProxy,
QNetTransformServerTokenProxy,
QChatServerTokenProxy,
IdentityServer.create,
Expand Down
Binary file not shown.
900 changes: 899 additions & 1 deletion package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion server/aws-lsp-codewhisperer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"postinstall": "node ./script/install_transitive_dep.js"
},
"dependencies": {
"@amazon/elastic-gumby-frontend-client": "file:../../core/atx-fes-client/amazon-elastic-gumby-frontend-client-1.0.0.tgz",
"@amzn/amazon-q-developer-streaming-client": "file:../../core/q-developer-streaming-client/amzn-amazon-q-developer-streaming-client-1.0.0.tgz",
"@amzn/codewhisperer": "file:../../core/codewhisperer/amzn-codewhisperer-1.0.0.tgz",
"@amzn/codewhisperer-runtime": "file:../../core/codewhisperer-runtime/amzn-codewhisperer-runtime-1.0.0.tgz",
Expand Down Expand Up @@ -106,6 +107,7 @@
"@amzn/codewhisperer",
"@amzn/codewhisperer-runtime",
"@amzn/codewhisperer-streaming",
"@amzn/amazon-q-developer-streaming-client"
"@amzn/amazon-q-developer-streaming-client",
"@amazon/elastic-gumby-frontend-client"
]
}
1 change: 1 addition & 0 deletions server/aws-lsp-codewhisperer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export * from './language-server/chat/qChatServer'
export * from './language-server/agenticChat/qAgenticChatServer'
export * from './shared/proxy-server'
export * from './language-server/netTransform/netTransformServer'
export * from './language-server/netTransform/atxNetTransformServer'
export { AmazonQServiceServerIAM, AmazonQServiceServerToken } from './shared/amazonQServer'
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import {
CancellationToken,
CredentialsProvider,
GetConfigurationFromServerParams,
InitializeParams,
Logging,
LSPErrorCodes,
ResponseError,
Server,
BearerCredentials,
} from '@aws/language-server-runtimes/server-interface'
import { AmazonQDeveloperProfile } from '../../shared/amazonQServiceManager/qDeveloperProfiles'
import { ElasticGumbyFrontendClient, ListAvailableProfilesCommand } from '@amazon/elastic-gumby-frontend-client'
import { DEFAULT_ATX_FES_ENDPOINT_URL } from '../../shared/constants'
import { getBearerTokenFromProvider } from '../../shared/utils'

// Transform Configuration Sections
export const TRANSFORM_PROFILES_CONFIGURATION_SECTION = 'aws.transformProfiles'

/**
* Transform Configuration Server - standalone server for ATX FES profile management
* Completely separate from qConfigurationServer to maintain clean RTS/ATX FES separation
*/
export class TransformConfigurationServer {
private atxClient: ElasticGumbyFrontendClient | null = null

constructor(
private readonly logging: Logging,
private readonly credentialsProvider: CredentialsProvider
) {}

/**
* Initialize as standalone LSP server
*/
async initialize(params: InitializeParams): Promise<any> {
return {
capabilities: {},
awsServerCapabilities: {
configurationProvider: {
sections: [TRANSFORM_PROFILES_CONFIGURATION_SECTION],
},
},
}
}

/**
* Handle configuration requests for Transform profiles
*/
async getConfiguration(params: GetConfigurationFromServerParams, token: CancellationToken): Promise<any> {
this.logging.log(`TransformConfigurationServer: Configuration requested for section: ${params.section}`)

switch (params.section) {
case TRANSFORM_PROFILES_CONFIGURATION_SECTION:
const profiles = await this.listAvailableProfiles(token)
return profiles
default:
throw new ResponseError(
LSPErrorCodes.RequestFailed,
`TransformConfigurationServer: Unsupported configuration section: ${params.section}`
)
}
}

/**
* Initialize ATX FES client with bearer token authentication
*/
private async initializeAtxClient(): Promise<boolean> {
try {
if (!this.credentialsProvider?.hasCredentials('bearer')) {
return false
}

const credentials = (await this.credentialsProvider.getCredentials('bearer')) as BearerCredentials
if (!credentials?.token) {
return false
}

// Initialize ATX FES client
this.atxClient = new ElasticGumbyFrontendClient({
region: 'us-east-1',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this region be dynamic? for example- if user has profile in FRA do we override client region somewhere else?

endpoint: DEFAULT_ATX_FES_ENDPOINT_URL,
})

return true
} catch (error) {
this.logging.error(`TransformConfigurationServer: Failed to initialize ATX client: ${error}`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets log url and region also here. in case of errors, it will help us investigate

return false
}
}

/**
* Add bearer token authentication to ATX FES command
*/
private async addBearerTokenToCommand(command: any): Promise<void> {
const credentials = (await this.credentialsProvider.getCredentials('bearer')) as BearerCredentials
if (!credentials?.token) {
throw new Error('No bearer token available for ATX FES authentication')
}

command.middlewareStack?.add(
(next: any) => async (args: any) => {
args.request.headers = {
...args.request.headers,
Authorization: `Bearer ${credentials.token}`,
}
return next(args)
},
{ step: 'build', priority: 'high' }
)
}

/**
* List available Transform profiles using ATX FES ListAvailableProfiles API
*/
async listAvailableProfiles(token: CancellationToken): Promise<AmazonQDeveloperProfile[]> {
try {
if (!this.atxClient && !(await this.initializeAtxClient())) {
this.logging.error('TransformConfigurationServer: Failed to initialize ATX FES client')
return []
}

const command = new ListAvailableProfilesCommand({
maxResults: 100,
})

await this.addBearerTokenToCommand(command)
const response = await this.atxClient!.send(command)

this.logging.log(
`TransformConfigurationServer: ATX FES returned ${response.profiles?.length || 0} profiles`
)

// Convert ATX FES profiles to AmazonQDeveloperProfile format
const transformProfiles: AmazonQDeveloperProfile[] = (response.profiles || []).map((profile: any) => {
const convertedProfile = {
arn: profile.arn || '',
name: profile.profileName || profile.applicationUrl || 'Unnamed Transform Profile',
applicationUrl: (profile.applicationUrl || '').replace(/\/$/, ''), // Strip trailing slash
identityDetails: {
region: profile.region || 'us-east-1',
accountId: profile.accountId || '',
},
}

return convertedProfile
})

return transformProfiles
} catch (error) {
this.logging.error(`TransformConfigurationServer: ListAvailableProfiles failed: ${error}`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be warning?

return []
}
}
}

/**
* Transform Configuration Server Token - creates standalone Transform configuration server
*/
export const TransformConfigurationServerToken = (): Server => {
return ({ credentialsProvider, lsp, logging }) => {
let transformConfigurationServer: TransformConfigurationServer

lsp.addInitializer(async params => {
transformConfigurationServer = new TransformConfigurationServer(logging, credentialsProvider)
return transformConfigurationServer.initialize(params)
})

lsp.extensions.onGetConfigurationFromServer(
async (params: GetConfigurationFromServerParams, token: CancellationToken) => {
return transformConfigurationServer.getConfiguration(params, token)
}
)

return () => {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets add unit tests for this new class.

CancellationToken,
ExecuteCommandParams,
InitializeParams,
Server,
} from '@aws/language-server-runtimes/server-interface'
import { AtxTokenServiceManager } from '../../shared/amazonQServiceManager/AtxTokenServiceManager'
import { ATXTransformHandler } from './atxTransformHandler'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:nit non-blocking. some variables use Atx and some use ATX. can we use only one convention


// ATX FES Commands
const AtxListAvailableProfilesCommand = 'aws/atxNetTransform/listAvailableProfiles'

// TODO: Phase 2 - Add remaining ATX FES APIs
// const AtxVerifySessionCommand = 'aws/atxNetTransform/verifySession' // LSP-only implementation
// const AtxListWorkspacesCommand = 'aws/atxNetTransform/listWorkspaces'
// const AtxCreateWorkspaceCommand = 'aws/atxNetTransform/createWorkspace'
// const AtxCreateJobCommand = 'aws/atxNetTransform/createJob'
// const AtxStartJobCommand = 'aws/atxNetTransform/startJob'
// const AtxGetJobCommand = 'aws/atxNetTransform/getJob'
// const AtxStopJobCommand = 'aws/atxNetTransform/stopJob'
// const AtxCreateUploadArtifactURLCommand = 'aws/atxNetTransform/createUploadArtifactURL'
// const AtxCompleteUploadArtifactURLCommand = 'aws/atxNetTransform/completeUploadArtifactURL'
// const AtxCreateDownloadArtifactURLCommand = 'aws/atxNetTransform/createDownloadArtifactURL'
// const AtxListArtifactsCommand = 'aws/atxNetTransform/listArtifacts'
// const AtxListJobStepPlansCommand = 'aws/atxNetTransform/listJobStepPlans'

// TODO: Phase 2 - Add remaining ATX FES APIs
export const AtxNetTransformServerToken =
(): Server =>
({ workspace, logging, lsp, telemetry, runtime }) => {
let atxTokenServiceManager: AtxTokenServiceManager
let atxTransformHandler: ATXTransformHandler

const runAtxTransformCommand = async (params: ExecuteCommandParams, _token: CancellationToken) => {
try {
switch (params.command) {
case AtxListAvailableProfilesCommand: {
const maxResults = (params as any).maxResults || 100
const response = await atxTransformHandler.listAvailableProfiles(maxResults)
return response
}
default: {
throw new Error(`Unknown ATX FES command: ${params.command}`)
}
}
} catch (e: any) {
logging.log(`ATX FES: Error executing command ${params.command}: ${e}`)
throw e
}
}

const onExecuteCommandHandler = async (
params: ExecuteCommandParams,
_token: CancellationToken
): Promise<any> => {
return runAtxTransformCommand(params, _token)
}

const onInitializeHandler = async (params: InitializeParams) => {
return {
capabilities: {
executeCommandProvider: {
commands: [
AtxListAvailableProfilesCommand,
// TODO: Phase 2: Add remaining ATX FES APIs
// AtxVerifySessionCommand, // LSP-only implementation
// AtxListWorkspacesCommand,
// AtxCreateWorkspaceCommand,
// AtxCreateJobCommand,
// AtxStartJobCommand,
// AtxGetJobCommand,
// AtxStopJobCommand,
// AtxCreateUploadArtifactURLCommand,
// AtxCompleteUploadArtifactURLCommand,
// AtxCreateDownloadArtifactURLCommand,
// AtxListArtifactsCommand,
// AtxListJobStepPlansCommand,
],
},
},
}
}

const onInitializedHandler = () => {
atxTokenServiceManager = AtxTokenServiceManager.getInstance()
atxTransformHandler = new ATXTransformHandler(atxTokenServiceManager, workspace, logging, runtime)

logging.log('ATX FES Server: Initialized')
}

lsp.addInitializer(onInitializeHandler)
lsp.onInitialized(onInitializedHandler)
lsp.onExecuteCommand(onExecuteCommandHandler)

return () => {}
}
Loading
Loading