Skip to content

Commit

Permalink
CLI: fix error handling for invalid access tokens (#5427)
Browse files Browse the repository at this point in the history
Fixes Cody-3261.
Fixes CODY-2967.
Fixes CODY-2909.

Previously, Cody CLI users got a poor experience if they had provided
expired credentials with `SRC_ACCESS_TOKEN`. The `cody chat` command
reported that the user was "not logged in" and `cody auth login`
confusingly reported that the user was already logged in.

Now, Cody CLI reports a helpful error message saying the user has
invalid credentials and documents how to fix the problem by creating a
new access token.

This fix ended up being harder to implement than I original expected
because the code related to authentication was badly organized.
Specifically, we didn't distinguish between error cases (invalid token)
and unauthenticate cases (the user hasn't logged in yet). I used this
issue as an opportunity to clean up the code so that all the validation
is done in a single function that handles all cases.


## Test plan

Manually tested the following flows

- `cody chat` works with environment variable login and secret storage
login
- `export SRC_ACCESS_TOKEN=foobar`, and confirm that `cody chat` and
`cody auth login` give helpful error messages. Also confirm this happens
when the secret storage has an invalid token (manually edited keychain
item)
```
❯ export SRC_ACCESS_TOKEN=foobar
❯ pnpm agent auth login
...
✖ The provided access token is invalid. The most common cause for this is that the access token has expired. If you are using SRC_ACCESS_TOKEN, create a new token at https://sourcegraph.sourcegraph.com/user/settings/tokens/new?description=CodyCLI and update the value of SRC_ACCESS_TOKEN. If you are using `cody auth login --web`, run `cody auth logout` and try logging in again.
```
- Confirm that `cody auth logout` reports a helpful error message when
`SRC_ACCESS_TOKEN` is set
```
❯ pnpm agent auth logout
...
✖ You cannot logout when using SRC_ACCESS_TOKEN with logout. To fix this problem, run `unset SRC_ACCESS_TOKEN` and try again.
```
<!-- Required. See
https://docs-legacy.sourcegraph.com/dev/background-information/testing_principles.
-->

## Changelog

* Cody CLI now reports a helpful error message when authenticating with
an invalid access token

<!-- OPTIONAL; info at
https://www.notion.so/sourcegraph/Writing-a-changelog-entry-dd997f411d524caabf0d8d38a24a878c
-->
olafurpg authored Sep 2, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 9b42cbd commit e1fe6a5
Showing 13 changed files with 292 additions and 227 deletions.
5 changes: 5 additions & 0 deletions agent/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -10,6 +10,11 @@ This is a log of all notable changes to the Cody command-line tool. [Unreleased]

### Changed

## 5.5.13
### Fixed

- `cody chat` and `cody auth login` now report a helpful error message when trying to authenticate with an invalid access token.

## 5.5.12

### Fixed
4 changes: 2 additions & 2 deletions agent/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sourcegraph/cody",
"version": "5.5.12",
"version": "5.5.13",
"description": "Cody CLI is the same technology that powers Cody in the IDE but available from the command-line.",
"license": "Apache-2.0",
"repository": {
@@ -17,7 +17,7 @@
"build:webviews": "pnpm -C ../vscode run -s _build:webviews --mode production --outDir ../../agent/dist/webviews",
"build": "pnpm run -s build:root && pnpm run -s build:webviews && pnpm run -s build:agent",
"build-minify": "pnpm run build --minify",
"agent": "pnpm run build && node --enable-source-maps dist/index.js",
"agent": "pnpm run build:for-tests && node --enable-source-maps dist/index.js",
"agent:skip-root-build": "pnpm run build:agent && node --enable-source-maps dist/index.js",
"agent:debug": "pnpm run build && CODY_AGENT_TRACE_PATH=/tmp/agent.json CODY_AGENT_DEBUG_REMOTE=true node --enable-source-maps ./dist/index.js api jsonrpc-stdio",
"build-ts": "tsc --build",
142 changes: 73 additions & 69 deletions agent/recordings/cody-chat_103640681/recording.har.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 1 addition & 8 deletions agent/src/cli/__snapshots__/command-chat.test.ts.snap
Original file line number Diff line number Diff line change
@@ -4,26 +4,19 @@ exports[`--context-file (animal test) 1`] = `
"command: cody chat chat --context-file animal.ts --show-context -m implement a
cow. Only print the code without any explanation.
exitCode: 0
stdout: >+
stdout: |+
> Context items:
> 1. WORKING_DIRECTORY/animal.ts
Here's the implementation of a cow based on the provided \`StrangeAnimal\` interface:
\`\`\`typescript:animal.ts
class Cow implements StrangeAnimal {
makesSound(): 'coo' | 'moo' {
return 'moo';
}
}
\`\`\`
stderr: ""
81 changes: 60 additions & 21 deletions agent/src/cli/command-auth/AuthenticatedAccount.ts
Original file line number Diff line number Diff line change
@@ -3,33 +3,23 @@ import type { Ora } from 'ora'
import { readCodySecret } from './secrets'

import type { CurrentUserInfo } from '@sourcegraph/cody-shared/src/sourcegraph-api/graphql/client'
import { isError } from 'lodash'
import type { AuthenticationOptions } from './command-login'
import { type Account, loadUserSettings } from './settings'

type AuthenticationSource = 'ENVIRONMENT_VARIABLE' | 'SECRET_STORAGE'

/**
* Wrapper around `Account` with the addition of an access token that's loaded
* from the OS keychain.
*/
export class AuthenticatedAccount {
public graphqlClient: SourcegraphGraphQLAPIClient

private userInfo: CurrentUserInfo | Error | null = null
private constructor(
public readonly account: Account,
public readonly accessToken: string
) {
this.graphqlClient = new SourcegraphGraphQLAPIClient({
accessToken: this.accessToken,
customHeaders: this.account.customHeaders,
serverEndpoint: this.account.serverEndpoint,
})
}

public async getCurrentUserInfo(): Promise<CurrentUserInfo | null | Error> {
if (!this.userInfo) {
this.userInfo = await this.graphqlClient.getCurrentUserInfo()
}
return this.userInfo
}
public readonly accessToken: string,
public readonly userInfo: CurrentUserInfo,
public readonly source: AuthenticationSource
) {}

get id(): string {
return this.account.id
@@ -39,7 +29,53 @@ export class AuthenticatedAccount {
return this.account.serverEndpoint
}

public static async fromUserSettings(spinner: Ora): Promise<AuthenticatedAccount | undefined> {
get username(): string {
return this.account.username
}

private static async fromCredentials(
options: AuthenticationOptions,
source: AuthenticationSource
): Promise<AuthenticatedAccount | Error> {
const graphqlClient = new SourcegraphGraphQLAPIClient({
accessToken: options.accessToken,
serverEndpoint: options.endpoint,
})
const userInfo = await graphqlClient.getCurrentUserInfo()
if (isError(userInfo)) {
return userInfo
}
if (!userInfo?.username) {
return new Error(
'failed to authenticated with credentials from environment variable SRC_ACCESS_TOKEN'
)
}
return new AuthenticatedAccount(
{
id: userInfo.username,
username: userInfo.username,
serverEndpoint: options.endpoint,
},
options.accessToken,
userInfo,
source
)
}

public static async fromUserSettings(
spinner: Ora,
environmentVariables: AuthenticationOptions
): Promise<AuthenticatedAccount | Error | undefined> {
if (environmentVariables.accessToken) {
const account = await AuthenticatedAccount.fromCredentials(
environmentVariables,
'ENVIRONMENT_VARIABLE'
)
if (isError(account)) {
return account
}
return account
}
const settings = loadUserSettings()
if (!settings.activeAccountID) {
return undefined
@@ -55,11 +91,14 @@ export class AuthenticatedAccount {
public static async fromUnauthenticated(
spinner: Ora,
account: Account
): Promise<AuthenticatedAccount | undefined> {
): Promise<AuthenticatedAccount | Error | undefined> {
const accessToken = await readCodySecret(spinner, account)
if (!accessToken) {
return undefined
}
return new AuthenticatedAccount(account, accessToken)
return AuthenticatedAccount.fromCredentials(
{ accessToken, endpoint: account.serverEndpoint },
'SECRET_STORAGE'
)
}
}
27 changes: 14 additions & 13 deletions agent/src/cli/command-auth/command-accounts.ts
Original file line number Diff line number Diff line change
@@ -4,13 +4,16 @@ import Table from 'easy-table'
import { isError } from 'lodash'
import ora from 'ora'
import { AuthenticatedAccount } from './AuthenticatedAccount'
import { booleanToText, failSpinner } from './command-auth'
import { notLoggedIn } from './messages'
import { booleanToText } from './command-auth'
import { type AuthenticationOptions, accessTokenOption, endpointOption } from './command-login'
import { notLoggedIn, unknownErrorSpinner } from './messages'
import { loadUserSettings } from './settings'

export const accountsCommand = new Command('accounts')
.description('Print all the authenticated accounts')
.action(async () => {
.addOption(accessTokenOption)
.addOption(endpointOption)
.action(async (options: AuthenticationOptions) => {
const spinner = ora('Loading active accounts')
try {
const settings = loadUserSettings()
@@ -20,25 +23,23 @@ export const accountsCommand = new Command('accounts')
}
const t = new Table()
for (const account of settings.accounts ?? []) {
const authenticated = await AuthenticatedAccount.fromUnauthenticated(spinner, account)
t.cell(chalk.bold('Name'), account.id)
t.cell(chalk.bold('Instance'), account.serverEndpoint)
const isActiveAccount = account.id === settings.activeAccountID
t.cell(chalk.bold('Active'), booleanToText(isActiveAccount))
const userInfo = await authenticated?.getCurrentUserInfo()
const isAuthenticated = Boolean(userInfo) && !isError(userInfo)
t.cell(chalk.bold('Authenticated'), booleanToText(isAuthenticated))
const authenticated = await AuthenticatedAccount.fromUnauthenticated(spinner, account)
t.cell(
chalk.bold('Authenticated'),
isError(authenticated)
? 'Invalid credentials'
: booleanToText(Boolean(authenticated))
)
t.newRow()
}
console.log(t.toString())
process.exit(0)
} catch (error) {
if (error instanceof Error) {
failSpinner(spinner, 'Failed to load active accounts', error)
} else {
spinner.prefixText = String(error)
spinner.fail('Failed to load active account')
}
unknownErrorSpinner(spinner, error, options)
process.exit(1)
}
})
6 changes: 0 additions & 6 deletions agent/src/cli/command-auth/command-auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Command } from 'commander'
import type { Ora } from 'ora'
import { accountsCommand } from './command-accounts'
import { loginCommand } from './command-login'
import { logoutCommand } from './command-logout'
@@ -25,8 +24,3 @@ export const authCommand = () =>
export function booleanToText(value: boolean): string {
return value ? 'Yes' : 'No'
}

export function failSpinner(spinner: Ora, text: string, error: Error): void {
spinner.prefixText = error.stack ?? error.message
spinner.fail(text)
}
83 changes: 44 additions & 39 deletions agent/src/cli/command-auth/command-login.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,58 @@
import http from 'node:http'
import { input, select } from '@inquirer/prompts'
import { SourcegraphGraphQLAPIClient, isError } from '@sourcegraph/cody-shared'
import { Command } from 'commander'
import { DOTCOM_URL, SourcegraphGraphQLAPIClient, isError } from '@sourcegraph/cody-shared'
import { Command, Option } from 'commander'
import open from 'open'
import ora from 'ora'
import type { Ora } from 'ora'
import { formatURL } from '../../../../vscode/src/auth/auth'
import { AuthenticatedAccount } from './AuthenticatedAccount'
import { errorSpinner, unknownErrorSpinner } from './messages'
import { writeCodySecret } from './secrets'
import { type Account, type UserSettings, loadUserSettings, writeUserSettings } from './settings'

interface LoginOptions {
export interface AuthenticationOptions {
accessToken: string
endpoint: string
}

export const DEFAULT_AUTHENTICATION_OPTIONS: AuthenticationOptions = {
accessToken: '',
endpoint: DOTCOM_URL.toString(),
}

export const accessTokenOption = new Option('--access-token <token>', 'Manually provide an access token')
.env('SRC_ACCESS_TOKEN')
.default('')

export const endpointOption = new Option(
'--endpoint <url>',
'Manually provide the URL of the Sourcegraph instance'
)
.env('SRC_ENDPOINT')
.default(DEFAULT_AUTHENTICATION_OPTIONS.endpoint)

interface LoginOptions extends AuthenticationOptions {
web: boolean
accessToken?: string
endpoint?: string
}

export const loginCommand = new Command('login')
.description('Log in to Sourcegraph')
.option('--web', 'Open a browser to authenticate')
.option(
'--access-token <token>',
'Manually provide an access token (env SRC_ACCESS_TOKEN)',
process.env.SRC_ACCESS_TOKEN
)
.option(
'--endpoint <url>',
'Manually provide a server endpoint (env SRC_ENDPOINT)',
process.env.SRC_ENDPOINT ?? 'https://sourcegraph.com/'
)
.addOption(accessTokenOption)
.addOption(endpointOption)
.action(async (options: LoginOptions) => {
const spinner = ora('Logging in...').start()
const account = await AuthenticatedAccount.fromUserSettings(spinner)
if (!spinner.isSpinning) {
const account = await AuthenticatedAccount.fromUserSettings(spinner, options)
if (isError(account)) {
errorSpinner(spinner, account, options)
process.exit(1)
}
const userInfo = await account?.getCurrentUserInfo()
if (!isError(userInfo) && userInfo?.username) {
spinner.succeed('You are already logged in as ' + userInfo.username)
if (account?.userInfo.username && account.source === 'SECRET_STORAGE') {
spinner.succeed('You are already logged in as ' + account.userInfo.username)
process.exit(0)
}

if (!options.web && !options.accessToken) {
spinner
.start()
@@ -54,31 +67,24 @@ export const loginCommand = new Command('login')
}
try {
const account = await loginAction(options, spinner)
if (!account) {
if (spinner.isSpinning) {
spinner.fail('Failed to authenticate')
}
if (isError(account)) {
errorSpinner(spinner, account, options)
process.exit(1)
}
const userInfo = await account.getCurrentUserInfo()
if (!userInfo || isError(userInfo)) {
spinner.fail(
`Failed to fetch username for account ${account.id} in ${account.serverEndpoint}`
if (!account?.userInfo.username) {
errorSpinner(
spinner,
new Error(`failed to authenticate with credentials ${JSON.stringify(options)}`),
options
)
process.exit(1)
}
spinner.succeed(
`Authenticated as ${userInfo.username} at Sourcegraph endpoint ${account.serverEndpoint}`
`Logged in as ${account.userInfo.username} at Sourcegraph endpoint ${account.serverEndpoint}. Run 'cody auth logout' to log out.`
)
process.exit(0)
} catch (error) {
if (error instanceof Error) {
spinner.suffixText = error.stack ?? ''
spinner.fail(error.message)
} else {
spinner.suffixText = String(error)
spinner.fail('Failed to login')
}
unknownErrorSpinner(spinner, error, options)
process.exit(1)
}
})
@@ -91,7 +97,7 @@ type LoginMethod = 'web-login' | 'cli-login'
async function loginAction(
options: LoginOptions,
spinner: Ora
): Promise<AuthenticatedAccount | undefined> {
): Promise<AuthenticatedAccount | Error | undefined> {
const loginMethod: LoginMethod =
options.web && options.accessToken
? // Ambiguous, the user provided both --web and --access-token
@@ -139,8 +145,7 @@ async function loginAction(
const newAccounts = [account, ...oldAccounts]
const newSettings: UserSettings = { accounts: newAccounts, activeAccountID: account.id }
writeUserSettings(newSettings)
const result = await AuthenticatedAccount.fromUserSettings(spinner)
return result
return AuthenticatedAccount.fromUserSettings(spinner, options)
}

/**
28 changes: 19 additions & 9 deletions agent/src/cli/command-auth/command-logout.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
import { Command } from 'commander'
import ora from 'ora'
import {
type AuthenticationOptions,
DEFAULT_AUTHENTICATION_OPTIONS,
accessTokenOption,
endpointOption,
} from './command-login'
import { unknownErrorSpinner } from './messages'
import { removeCodySecret } from './secrets'
import { loadUserSettings, userSettingsPath, writeUserSettings } from './settings'

export const logoutCommand = new Command('logout')
.description('Log out of Sourcegraph')
.action(async () => {
const spinner = ora('Loading active accounts')
.addOption(accessTokenOption)
.addOption(endpointOption)
.action(async (options: AuthenticationOptions) => {
const spinner = ora()
if (options.accessToken) {
spinner.fail(
'You cannot logout when using SRC_ACCESS_TOKEN with logout. To fix this problem, run `unset SRC_ACCESS_TOKEN` and try again.'
)
process.exit(1)
}
spinner.text = 'Loading active accounts'
try {
const settings = loadUserSettings()
if (!settings?.accounts || settings.accounts.length === 0 || !settings.activeAccountID) {
@@ -30,13 +46,7 @@ export const logoutCommand = new Command('logout')
spinner.succeed(`Logged out of account ${account.username} on ${account.serverEndpoint}`)
process.exit(0)
} catch (error) {
if (error instanceof Error) {
spinner.prefixText = error.stack ?? ''
spinner.fail(error.message)
} else {
spinner.prefixText = String(error)
spinner.fail('Failed to load active account')
}
unknownErrorSpinner(spinner, error, DEFAULT_AUTHENTICATION_OPTIONS)
process.exit(1)
}
})
37 changes: 14 additions & 23 deletions agent/src/cli/command-auth/command-whoami.ts
Original file line number Diff line number Diff line change
@@ -2,39 +2,30 @@ import { Command } from 'commander'
import { isError } from 'lodash'
import ora from 'ora'
import { AuthenticatedAccount } from './AuthenticatedAccount'
import { failSpinner } from './command-auth'
import { notLoggedIn } from './messages'
import { type AuthenticationOptions, accessTokenOption, endpointOption } from './command-login'
import { errorSpinner, notLoggedIn, unknownErrorSpinner } from './messages'

export const whoamiCommand = new Command('whoami')
.description('Print the active authenticated account')
.action(async () => {
.addOption(accessTokenOption)
.addOption(endpointOption)
.action(async (options: AuthenticationOptions) => {
const spinner = ora('Loading active account')
try {
const account = await AuthenticatedAccount.fromUserSettings(spinner)
if (!account) {
notLoggedIn(spinner)
const account = await AuthenticatedAccount.fromUserSettings(spinner, options)
if (isError(account)) {
errorSpinner(spinner, account, options)
process.exit(1)
}
const userInfo = await account.getCurrentUserInfo()
if (!userInfo || isError(userInfo)) {
failSpinner(
spinner,
`Failed to fetch username for account ${account.id} in ${account.serverEndpoint}`,
userInfo ?? new Error('no authenticated user')
)
if (!account?.username) {
notLoggedIn(spinner)
process.exit(1)
} else {
spinner.succeed(`Authenticated as ${userInfo.username} on ${account.serverEndpoint}`)
process.exit(0)
}

spinner.succeed(`Authenticated as ${account.username} on ${account.serverEndpoint}`)
process.exit(0)
} catch (error) {
if (error instanceof Error) {
spinner.prefixText = error.stack ?? ''
spinner.fail(error.message)
} else {
spinner.prefixText = String(error)
spinner.fail('Failed to load active account')
}
unknownErrorSpinner(spinner, error, options)
process.exit(1)
}
})
24 changes: 24 additions & 0 deletions agent/src/cli/command-auth/messages.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,32 @@
import type { Ora } from 'ora'
import type { AuthenticationOptions } from './command-login'

export function notLoggedIn(spinner: Ora): void {
if (!spinner.isSpinning) {
return
}
spinner.fail('Not logged in. To fix this problem, run:\n\tcody auth login --web')
}

export function errorSpinner(spinner: Ora, error: Error, options: AuthenticationOptions): void {
if (error.message.includes('Invalid access token')) {
const createNewTokenURL = options.endpoint + '/user/settings/tokens/new?description=CodyCLI'
spinner.fail(
'The provided access token is invalid. ' +
'The most common cause for this is that the access token has expired. ' +
`If you are using SRC_ACCESS_TOKEN, create a new token at ${createNewTokenURL} and update the value of SRC_ACCESS_TOKEN. ` +
'If you are using `cody auth login --web`, run `cody auth logout` and try logging in again. '
)
} else {
spinner.suffixText = error.stack ?? ''
spinner.fail(error.message)
}
}

export function unknownErrorSpinner(spinner: Ora, error: unknown, options: AuthenticationOptions): void {
if (error instanceof Error) {
errorSpinner(spinner, error, options)
} else {
spinner.fail(String(error))
}
}
15 changes: 10 additions & 5 deletions agent/src/cli/command-chat.test.ts
Original file line number Diff line number Diff line change
@@ -30,13 +30,18 @@ describe('cody chat', () => {
args: string[]
expectedExitCode?: number
}): Promise<ChatCommandResult> {
// For some reason, I can't get the option parsing working with
// `--access-token` or `--endpoint` so we modify process.env instead.
process.env.SRC_ACCESS_TOKEN = credentials.token ?? credentials.redactedToken
process.env.SRC_ENDPOINT = credentials.serverEndpoint
process.env.DISABLE_FEATURE_FLAGS = 'true'
process.env.CODY_TELEMETRY_EXPORTER = 'testing'
const args = [...params.args, '--dir', tmp.rootPath, '--silent']
const args = [
...params.args,
'--dir',
tmp.rootPath,
'--endpoint',
credentials.serverEndpoint,
'--access-token',
credentials.token ?? credentials.redactedToken,
'--silent',
]

const command = chatCommand()
const parseResult = command.parseOptions(args)
58 changes: 26 additions & 32 deletions agent/src/cli/command-chat.ts
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import { type ContextItem, ModelUsage, TokenCounterUtils, isDotCom } from '@sour
import { Command } from 'commander'

import Table from 'easy-table'
import { isError } from 'lodash'
import * as vscode from 'vscode'
import type { ExtensionTranscriptMessage } from '../../../vscode/src/chat/protocol'
import { activate } from '../../../vscode/src/extension.node'
@@ -17,13 +18,16 @@ import type { ClientInfo } from '../protocol-alias'
import { Streams } from './Streams'
import { codyCliClientName } from './codyCliClientName'
import { AuthenticatedAccount } from './command-auth/AuthenticatedAccount'
import { notLoggedIn } from './command-auth/messages'
import {
type AuthenticationOptions,
accessTokenOption,
endpointOption,
} from './command-auth/command-login'
import { errorSpinner, notLoggedIn } from './command-auth/messages'
import { isNonEmptyArray } from './isNonEmptyArray'

declare const process: { pkg: { entrypoint: string } } & NodeJS.Process
export interface ChatOptions {
endpoint: string
accessToken: string
export interface ChatOptions extends AuthenticationOptions {
message: string
stdin?: boolean
messageArgs?: string[]
@@ -59,16 +63,8 @@ Enterprise Only:
// `string[]`, which is not what we want. This means that cody chat
// --help does not document you can pass arguments, it will just
// silently work.
.option(
'--endpoint <url>',
'Sourcegraph instance URL',
process.env.SRC_ENDPOINT ?? 'https://sourcegraph.com'
)
.option(
'--access-token <token>',
'Sourcegraph access token. ' + loginInstruction,
process.env.SRC_ACCESS_TOKEN ?? ''
)
.addOption(accessTokenOption)
.addOption(endpointOption)
.option('-C, --dir <dir>', 'Run in directory <dir>', process.cwd())
.option('--model <model>', 'Chat model to use')
.option(
@@ -86,18 +82,18 @@ Enterprise Only:
.option('--debug', 'Enable debug logging', false)
.action(async (options: ChatOptions, cmd) => {
options.messageArgs = cmd.args
if (!options.accessToken) {
const spinner = ora().start('Loading access token')
const account = await AuthenticatedAccount.fromUserSettings(spinner)
if (!spinner.isSpinning) {
process.exit(1)
}
spinner.stop()
if (account) {
options.accessToken = account.accessToken
options.endpoint = account.serverEndpoint
}
const spinner = ora().start('Logging in')
const account = await AuthenticatedAccount.fromUserSettings(spinner, options)
if (isError(account)) {
errorSpinner(spinner, account, options)
process.exit(1)
}
if (!account?.username) {
notLoggedIn(spinner)
process.exit(1)
}
options.accessToken = account.accessToken
options.endpoint = account.serverEndpoint
let polly: Polly | undefined
if (process.env.CODY_RECORDING_DIRECTORY && process.env.CODY_RECORDING_NAME) {
polly = startPollyRecording({
@@ -114,8 +110,6 @@ Enterprise Only:
process.exit(exitCode)
})

const loginInstruction = 'Sign in with the command: cody auth login --web'

export async function chatAction(options: ChatOptions): Promise<number> {
const streams = options.streams ?? Streams.default()
const spinner = ora({
@@ -149,6 +143,11 @@ export async function chatAction(options: ChatOptions): Promise<number> {
}
spinner.text = 'Initializing...'
const { serverInfo, client, messageHandler } = await newEmbeddedAgentClient(clientInfo, activate)
if (!serverInfo.authStatus?.isLoggedIn) {
notLoggedIn(spinner)
return 1
}

const { models } = await client.request('chat/models', { modelUsage: ModelUsage.Chat })

if (options.debug) {
@@ -157,11 +156,6 @@ export async function chatAction(options: ChatOptions): Promise<number> {
})
}

if (!serverInfo.authStatus?.isLoggedIn) {
notLoggedIn(spinner)
return 1
}

const endpoint = serverInfo.authStatus.endpoint ?? options.endpoint
const tokenSource = new vscode.CancellationTokenSource()
const token = tokenSource.token

0 comments on commit e1fe6a5

Please sign in to comment.