Skip to content

Commit 90f0246

Browse files
refactor(cli): move validateAgent from index.ts to cli.ts; update tests to import from cli; centralize agent validation and startup display logic
🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
1 parent 59ff0cc commit 90f0246

File tree

3 files changed

+81
-64
lines changed

3 files changed

+81
-64
lines changed

npm-app/src/__tests__/validate-agent-passthrough.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
mock,
99
} from 'bun:test'
1010

11-
import { validateAgent } from '../index'
11+
import { validateAgent } from '../cli'
1212
import * as SpinnerMod from '../utils/spinner'
1313

1414
describe('validateAgent agent pass-through', () => {

npm-app/src/cli.ts

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@ import {
2222
gray,
2323
green,
2424
magenta,
25+
red,
2526
yellow,
2627
} from 'picocolors'
2728

2829
import { loadLocalAgents, loadedAgents } from './agents/load-agents'
30+
import { backendUrl } from './config'
31+
import { createAuthHeaders } from './utils/auth-headers'
2932
import {
3033
killAllBackgroundProcesses,
3134
sendKillSignalToAllBackgroundProcesses,
@@ -146,6 +149,72 @@ function getCachedLocalAgentInfo(): Record<
146149
return cachedLocalAgentInfo
147150
}
148151

152+
/**
153+
* Validates an agent name against local and remote agents
154+
* @param agent The agent name to validate
155+
* @param localAgents Optional local agents to check against
156+
* @returns The display name of the agent if valid, undefined otherwise
157+
*/
158+
export async function validateAgent(
159+
agent: string,
160+
localAgents?: Record<string, any>,
161+
): Promise<string | undefined> {
162+
const agents = localAgents ?? {}
163+
164+
// if local agents are loaded, they're already validated
165+
const localById = agents?.[agent]
166+
const localByDisplay = Object.values(agents ?? {}).find(
167+
(a: any) => a?.displayName === agent,
168+
)
169+
if (localById || localByDisplay) {
170+
// Display the resolved agent name for local agents too
171+
const displayName = (localById?.displayName ||
172+
localByDisplay?.displayName ||
173+
localById?.id ||
174+
agent) as string
175+
// Delete the inline console.log to centralize logging in the caller
176+
return displayName
177+
}
178+
179+
Spinner.get().start('Checking agent...')
180+
try {
181+
const url = `${backendUrl}/api/agents/validate-name?agentId=${encodeURIComponent(agent)}`
182+
183+
// Use helper to create headers with x-codebuff-api-key
184+
const headers = createAuthHeaders()
185+
186+
const resp = await fetch(url, {
187+
method: 'GET',
188+
headers,
189+
})
190+
// Include optional fields from backend, notably displayName
191+
const data: {
192+
valid?: boolean
193+
normalizedId?: string
194+
displayName?: string
195+
} = await resp.json().catch(() => ({}) as any)
196+
197+
if (resp.ok && data.valid) {
198+
// Delete inline console logging here to centralize in caller
199+
return data.displayName
200+
}
201+
202+
if (resp.ok && !data.valid) {
203+
console.error(red(`\nUnknown agent: ${bold(agent)}. Exiting.`))
204+
process.exit(1)
205+
}
206+
} catch {
207+
console.error(
208+
yellow(
209+
`\nCould not validate agent due to a network error. Proceeding...`,
210+
),
211+
)
212+
} finally {
213+
Spinner.get().stop()
214+
}
215+
return undefined
216+
}
217+
149218
const PROMPT_HISTORY_PATH = path.join(CONFIG_DIR, 'prompt_history.json')
150219

151220
// Paste detection constants
@@ -631,10 +700,16 @@ export class CLI {
631700
} else {
632701
// Normal interactive mode
633702
if (client.user) {
634-
displayGreeting(this.costMode, client.user.name)
703+
// Validate agent and display name before greeting if agent is specified
704+
if (this.agent) {
705+
const agents = await loadLocalAgents({ verbose: false })
706+
const resolvedName = await validateAgent(this.agent, agents)
707+
if (resolvedName) {
708+
console.log(green(`\nAgent: ${bold(resolvedName)}`))
709+
}
710+
}
635711

636-
// Agent name will be displayed by validateAgent when resolved
637-
// No need to display here to avoid race conditions
712+
displayGreeting(this.costMode, client.user.name)
638713
} else {
639714
console.log(
640715
`Welcome to Codebuff! Give us a sec to get your account set up...`,

npm-app/src/index.ts

Lines changed: 2 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -25,66 +25,11 @@ import { logAndHandleStartup } from './startup-process-handler'
2525
import { recreateShell } from './terminal/run-command'
2626
import { validateAgentDefinitionsIfAuthenticated } from './utils/agent-validation'
2727
import { initAnalytics, trackEvent } from './utils/analytics'
28-
import { createAuthHeaders } from './utils/auth-headers'
2928
import { logger } from './utils/logger'
30-
import { Spinner } from './utils/spinner'
3129

3230
import type { CliOptions } from './types'
3331

34-
export async function validateAgent(
35-
agent: string,
36-
localAgents?: Record<string, any>,
37-
): Promise<string | undefined> {
38-
const agents = localAgents ?? {}
3932

40-
// if local agents are loaded, they're already validated
41-
if (
42-
!!agents?.[agent] ||
43-
!!Object.values(agents ?? {}).find((a: any) => a?.displayName === agent)
44-
)
45-
return
46-
47-
Spinner.get().start('Checking agent...')
48-
try {
49-
const url = `${backendUrl}/api/agents/validate-name?agentId=${encodeURIComponent(agent)}`
50-
51-
// Use helper to create headers with x-codebuff-api-key
52-
const headers = createAuthHeaders()
53-
54-
const resp = await fetch(url, {
55-
method: 'GET',
56-
headers,
57-
})
58-
// Include optional fields from backend, notably displayName
59-
const data: {
60-
valid?: boolean
61-
normalizedId?: string
62-
displayName?: string
63-
} = await resp.json().catch(() => ({}) as any)
64-
65-
if (resp.ok && data.valid) {
66-
// Console log the agent name immediately when resolved
67-
if (data.displayName) {
68-
console.log(green(`\nAgent: ${bold(data.displayName)}`))
69-
}
70-
return data.displayName
71-
}
72-
73-
if (resp.ok && !data.valid) {
74-
console.error(red(`\nUnknown agent: ${bold(agent)}. Exiting.`))
75-
process.exit(1)
76-
}
77-
} catch {
78-
console.error(
79-
yellow(
80-
`\nCould not validate agent due to a network error. Proceeding...`,
81-
),
82-
)
83-
} finally {
84-
Spinner.get().stop()
85-
}
86-
return undefined
87-
}
8833

8934
async function codebuff({
9035
initialInput,
@@ -111,19 +56,16 @@ async function codebuff({
11156

11257
const initFileContextPromise = initProjectFileContextWithWorker(projectRoot)
11358

114-
// Ensure validation runs strictly after local agent load/display
59+
// Load agents and validate definitions
11560
const loadAndValidatePromise: Promise<void> = loadLocalAgents({
11661
verbose: true,
117-
}).then(async (agents) => {
62+
}).then((agents) => {
11863
validateAgentDefinitionsIfAuthenticated(Object.values(agents))
11964

12065
const codebuffConfig = loadCodebuffConfig()
12166
if (!agent) {
12267
displayLoadedAgents(codebuffConfig)
123-
return
12468
}
125-
126-
await validateAgent(agent, agents)
12769
})
12870

12971
const readyPromise = Promise.all([

0 commit comments

Comments
 (0)