Skip to content

Commit

Permalink
Initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
pkukielka committed Jan 3, 2025
1 parent f3fe8b6 commit 068277d
Show file tree
Hide file tree
Showing 40 changed files with 438 additions and 138 deletions.
10 changes: 9 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,13 @@
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"rust-analyzer.procMacro.ignored": { "napi-derive": ["napi"] }
"rust-analyzer.procMacro.ignored": {
"napi-derive": [
"napi"
]
},
"debug.javascript.defaultRuntimeExecutable": {
"pwa-node": "/Users/pkukielka/.local/share/mise/shims/node"
},
"python.defaultInterpreterPath": "/Users/pkukielka/.local/share/mise/shims/python"
}
68 changes: 68 additions & 0 deletions agent/scripts/reverse-proxy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from aiohttp import web, ClientSession
from urllib.parse import urlparse
import asyncio

target_url = ''
port = 5050

async def proxy_handler(request):
async with ClientSession(auto_decompress=False) as session:
print(f'Request to: {request.url}')

# Modify headers here
headers = dict(request.headers)

# Reset the Host header to use target server host instead of the proxy host
if 'Host' in headers:
headers['Host'] = urlparse(target_url).netloc.split(':')[0]

# 'chunked' encoding results in error 400 from Cloudflare, removing it still keeps response chunked anyway
if 'Transfer-Encoding' in headers:
del headers['Transfer-Encoding']

# Use value of 'Authorization: Bearer' to fill 'X-Forwarded-User' and remove 'Authorization' header
if 'Authorization' in headers:
values = headers['Authorization'].split()
if values and values[0] == 'Bearer':
headers['X-Forwarded-User'] = values[1]
del headers['Authorization']

# Forward the request to target
async with session.request(
method=request.method,
url=f'{target_url}{request.path_qs}',
headers=headers,
data=await request.read()
) as response:
proxy_response = web.StreamResponse(
status=response.status,
headers=response.headers
)

await proxy_response.prepare(request)

# Stream the response back
async for chunk in response.content.iter_chunks():
await proxy_response.write(chunk[0])

await proxy_response.write_eof()
return proxy_response

app = web.Application()
app.router.add_route('*', '/{path_info:.*}', proxy_handler)


if __name__ == '__main__':
print('Usage: python reverse_proxy.py [target_url] [proxy_port]')

import sys
if (len(sys.argv) < 2):
print('Please specify target_url')
sys.exit(1)
if len(sys.argv) > 1:
target_url = sys.argv[1]
if len(sys.argv) > 2:
port = int(sys.argv[2])

print(f'Starting proxy server on port {port} targeting {target_url}...')
web.run_app(app, port=port)
12 changes: 11 additions & 1 deletion agent/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import path from 'node:path'
import type { Polly, Request } from '@pollyjs/core'
import {
type AccountKeyedChatHistory,
AuthCredentials,
type ChatHistoryKey,
type ClientCapabilities,
type CodyCommand,
Expand All @@ -14,6 +15,7 @@ import {
currentAuthStatusAuthed,
firstNonPendingAuthStatus,
firstResultFromOperation,
resolveAuth,
resolvedConfig,
telemetryRecorder,
waitUntilComplete,
Expand Down Expand Up @@ -70,6 +72,7 @@ import type { FixupActor, FixupFileCollection } from '../../vscode/src/non-stop/
import type { FixupControlApplicator } from '../../vscode/src/non-stop/strategies'
import { authProvider } from '../../vscode/src/services/AuthProvider'
import { localStorage } from '../../vscode/src/services/LocalStorageProvider'
import { secretStorage } from '../../vscode/src/services/SecretStorageProvider'
import { AgentWorkspaceEdit } from '../../vscode/src/testutils/AgentWorkspaceEdit'
import { AgentAuthHandler } from './AgentAuthHandler'
import { AgentFixupControls } from './AgentFixupControls'
Expand Down Expand Up @@ -1482,6 +1485,11 @@ export class Agent extends MessageHandler implements ExtensionClient {
return this.clientInfo?.capabilities ?? undefined
}

private async resolveAuthCredentials(extCfg: ExtensionConfiguration): Promise<AuthCredentials> {
const config = JSON.parse(extCfg.customConfigurationJson ?? '{}')
return resolveAuth(extCfg.serverEndpoint, config, secretStorage)
}

private async handleConfigChanges(
config: ExtensionConfiguration,
params?: { forceAuthentication: boolean }
Expand All @@ -1500,7 +1508,9 @@ export class Agent extends MessageHandler implements ExtensionClient {
},
auth: {
serverEndpoint: config.serverEndpoint,
accessToken: config.accessToken ?? null,
accessTokenOrHeaders:
config.accessToken ??
(await this.resolveAuthCredentials(config)).accessTokenOrHeaders,
},
clientState: {
anonymousUserID: config.anonymousUserID ?? null,
Expand Down
2 changes: 1 addition & 1 deletion agent/src/cli/command-auth/AuthenticatedAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class AuthenticatedAccount {
): Promise<AuthenticatedAccount | Error> {
const graphqlClient = SourcegraphGraphQLAPIClient.withStaticConfig({
configuration: { telemetryLevel: 'agent' },
auth: { accessToken: options.accessToken, serverEndpoint: options.endpoint },
auth: { accessTokenOrHeaders: options.accessToken, serverEndpoint: options.endpoint },
clientState: { anonymousUserID: null },
})
const userInfo = await graphqlClient.getCurrentUserInfo()
Expand Down
4 changes: 2 additions & 2 deletions agent/src/cli/command-auth/command-login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ async function loginAction(
: await captureAccessTokenViaBrowserRedirect(serverEndpoint, spinner)
const client = SourcegraphGraphQLAPIClient.withStaticConfig({
configuration: { telemetryLevel: 'agent' },
auth: { accessToken: token, serverEndpoint: serverEndpoint },
auth: { accessTokenOrHeaders: token, serverEndpoint: serverEndpoint },
clientState: { anonymousUserID: null },
})
const userInfo = await client.getCurrentUserInfo()
Expand Down Expand Up @@ -256,7 +256,7 @@ async function promptUserAboutLoginMethod(spinner: Ora, options: LoginOptions):
try {
const client = SourcegraphGraphQLAPIClient.withStaticConfig({
configuration: { telemetryLevel: 'agent' },
auth: { accessToken: options.accessToken, serverEndpoint: options.endpoint },
auth: { accessTokenOrHeaders: options.accessToken, serverEndpoint: options.endpoint },
clientState: { anonymousUserID: null },
})
const userInfo = await client.getCurrentUserInfo()
Expand Down
2 changes: 1 addition & 1 deletion agent/src/cli/command-bench/command-bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ export const benchCommand = new commander.Command('bench')
setStaticResolvedConfigurationWithAuthCredentials({
configuration: { customHeaders: {} },
auth: {
accessToken: options.srcAccessToken,
accessTokenOrHeaders: options.srcAccessToken,
serverEndpoint: options.srcEndpoint,
},
})
Expand Down
2 changes: 1 addition & 1 deletion agent/src/cli/command-bench/llm-judge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class LlmJudge {
localStorage.setStorage('noop')
setStaticResolvedConfigurationWithAuthCredentials({
configuration: { customHeaders: undefined },
auth: { accessToken: options.srcAccessToken, serverEndpoint: options.srcEndpoint },
auth: { accessTokenOrHeaders: options.srcAccessToken, serverEndpoint: options.srcEndpoint },
})
setClientCapabilities({ configuration: {}, agentCapabilities: undefined })
this.client = new SourcegraphNodeCompletionsClient()
Expand Down
5 changes: 4 additions & 1 deletion agent/src/local-e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,10 @@ export class LocalSGInstance {
// for checking the LLM configuration section.
this.gqlclient = SourcegraphGraphQLAPIClient.withStaticConfig({
configuration: { customHeaders: headers, telemetryLevel: 'agent' },
auth: { accessToken: this.params.accessToken, serverEndpoint: this.params.serverEndpoint },
auth: {
accessTokenOrHeaders: this.params.accessToken,
serverEndpoint: this.params.serverEndpoint,
},
clientState: { anonymousUserID: null },
})
}
Expand Down
8 changes: 7 additions & 1 deletion agent/src/vscode-shim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,15 @@ export function isAuthenticationChange(newConfig: ExtensionConfiguration): boole
return true
}

function getExternalAuthProvidersConfig(cfg: ExtensionConfiguration) {
return JSON.parse(cfg.customConfigurationJson ?? '{}')?.cody?.auth?.externalProviders
}

return (
extensionConfiguration.accessToken !== newConfig.accessToken ||
extensionConfiguration.serverEndpoint !== newConfig.serverEndpoint
extensionConfiguration.serverEndpoint !== newConfig.serverEndpoint ||
getExternalAuthProvidersConfig(extensionConfiguration) !==
getExternalAuthProvidersConfig(newConfig)
)
}

Expand Down
23 changes: 21 additions & 2 deletions lib/shared/src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ import type { ReadonlyDeep } from './utils'
* A redirect flow is initiated by the user clicking a link in the browser, while a paste flow is initiated by the user
* manually entering the access from into the VsCode App.
*/
export type TokenSource = 'redirect' | 'paste'
export type TokenSource = 'redirect' | 'paste' | 'custom-auth-provider'

export type AuthHeaders = Record<string, string>

/**
* The user's authentication credentials, which are stored separately from the rest of the
* configuration.
*/
export interface AuthCredentials {
serverEndpoint: string
accessToken: string | null
tokenSource?: TokenSource | undefined
accessTokenOrHeaders: string | AuthHeaders | null
}

export interface AutoEditsTokenLimit {
Expand Down Expand Up @@ -71,6 +73,20 @@ export interface AgenticContextConfiguration {
}
}

export interface ExternalAuthCommand {
commandLine: string[]
environment?: Record<string, string>
workingDir?: string
shell?: string
timeout?: number
windowsHide?: boolean
}

export interface ExternalAuthProvider {
endpoint: string
executable: ExternalAuthCommand
}

interface RawClientConfiguration {
net: NetConfiguration
codebase?: string
Expand Down Expand Up @@ -166,6 +182,9 @@ interface RawClientConfiguration {
*/
overrideServerEndpoint?: string | undefined
overrideAuthToken?: string | undefined

// External auth providers
authExternalProviders?: ExternalAuthProvider[]
}

/**
Expand Down
Loading

0 comments on commit 068277d

Please sign in to comment.