Skip to content

Commit 4152155

Browse files
committed
feat(cli): update default model to Kimi K2.6 for opencode init
1 parent fa68c41 commit 4152155

12 files changed

Lines changed: 1858 additions & 0 deletions

File tree

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
/**
2+
* API key handling logic for code commands
3+
* Handles key selection, creation, and rotation
4+
*/
5+
6+
import chalk from 'chalk'
7+
import readline from 'readline'
8+
import { ApiKeyService, CreateApiKeyOptions } from '../../services/api-key-service'
9+
import { AuthService } from '../../services/auth-service'
10+
import { COMMAND_GROUPS, SUBCOMMANDS } from '../../constants/command-structure'
11+
import { handleError } from '../../utils/error-handler'
12+
import { confirm, getInput } from './helpers'
13+
import type { ApiKeyResult, CodeCommandOptions } from './types'
14+
15+
/**
16+
* Check authentication status
17+
* Returns true if authenticated or has API key in env
18+
*/
19+
export async function checkAuthentication(): Promise<{
20+
authenticated: boolean
21+
hasEnvKey: boolean
22+
}> {
23+
// Check if we have an API key in environment first
24+
if (process.env.BERGET_API_KEY) {
25+
return { authenticated: true, hasEnvKey: true }
26+
}
27+
28+
// Only require authentication if we don't have an API key
29+
try {
30+
const authService = AuthService.getInstance()
31+
await authService.whoami()
32+
return { authenticated: true, hasEnvKey: false }
33+
} catch {
34+
return { authenticated: false, hasEnvKey: false }
35+
}
36+
}
37+
38+
/**
39+
* Print authentication error and guidance
40+
*/
41+
export function printAuthenticationError(): void {
42+
console.log(chalk.red('❌ Not authenticated with Berget AI.'))
43+
console.log(chalk.blue('To get started, you have two options:'))
44+
console.log('')
45+
console.log(chalk.yellow('Option 1: Use an existing API key (recommended)'))
46+
console.log(chalk.cyan(' Set BERGET_API_KEY environment variable:'))
47+
console.log(chalk.dim(' export BERGET_API_KEY=your_api_key_here'))
48+
console.log(chalk.cyan(' Or create a .env file in your project:'))
49+
console.log(chalk.dim(' echo "BERGET_API_KEY=your_api_key_here" > .env'))
50+
console.log('')
51+
console.log(chalk.yellow('Option 2: Login and create a new API key'))
52+
console.log(chalk.cyan(' berget auth login'))
53+
console.log(chalk.cyan(` berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT}`))
54+
console.log('')
55+
console.log(chalk.blue('Then try again.'))
56+
}
57+
58+
/**
59+
* Handle API key selection or creation
60+
* Returns the API key and key name
61+
*/
62+
export async function handleApiKeySelection(
63+
options: CodeCommandOptions,
64+
projectName: string
65+
): Promise<ApiKeyResult | null> {
66+
// Check for environment variable first (regardless of automation mode)
67+
if (process.env.BERGET_API_KEY) {
68+
console.log(chalk.blue('🔑 Using BERGET_API_KEY from environment'))
69+
return {
70+
apiKey: process.env.BERGET_API_KEY,
71+
keyName: `env-key-${projectName}`,
72+
}
73+
}
74+
75+
try {
76+
const apiKeyService = ApiKeyService.getInstance()
77+
78+
// List existing API keys
79+
if (!options.yes) {
80+
console.log(chalk.blue('\n📋 Checking existing API keys...'))
81+
}
82+
const existingKeys = await apiKeyService.list()
83+
84+
if (existingKeys.length > 0 && !options.yes) {
85+
return await selectExistingOrCreateNew(
86+
apiKeyService,
87+
existingKeys,
88+
projectName,
89+
options
90+
)
91+
} else {
92+
// No existing keys or automation mode - create new one
93+
return await createNewKey(apiKeyService, projectName, options)
94+
}
95+
} catch (error) {
96+
if (process.env.BERGET_API_KEY) {
97+
console.log(
98+
chalk.yellow(
99+
'⚠️ Could not verify API key with Berget API, but continuing with environment key'
100+
)
101+
)
102+
console.log(
103+
chalk.dim('This might be due to network issues or an invalid key')
104+
)
105+
return {
106+
apiKey: process.env.BERGET_API_KEY,
107+
keyName: `env-key-${projectName}`,
108+
}
109+
}
110+
111+
printApiKeyError(error)
112+
return null
113+
}
114+
}
115+
116+
/**
117+
* Select an existing key or create a new one
118+
*/
119+
async function selectExistingOrCreateNew(
120+
apiKeyService: ApiKeyService,
121+
existingKeys: Array<{
122+
id: number
123+
name: string
124+
prefix: string
125+
created: string
126+
lastUsed: string | null
127+
}>,
128+
projectName: string,
129+
options: CodeCommandOptions
130+
): Promise<ApiKeyResult | null> {
131+
console.log(chalk.blue('Found existing API keys:'))
132+
console.log(chalk.dim('─'.repeat(60)))
133+
134+
existingKeys.forEach((key, index) => {
135+
console.log(
136+
`${chalk.cyan((index + 1).toString())}. ${chalk.bold(key.name)} (${key.prefix}...)`
137+
)
138+
console.log(
139+
chalk.dim(
140+
` Created: ${new Date(key.created).toLocaleDateString('sv-SE')}`
141+
)
142+
)
143+
console.log(
144+
chalk.dim(
145+
` Last used: ${key.lastUsed ? new Date(key.lastUsed).toLocaleDateString('sv-SE') : 'Never'}`
146+
)
147+
)
148+
if (index < existingKeys.length - 1) console.log()
149+
})
150+
151+
console.log(chalk.dim('─'.repeat(60)))
152+
console.log(chalk.cyan(`${existingKeys.length + 1}. Create a new API key`))
153+
154+
// Get user choice
155+
const choice = await getUserChoice(existingKeys.length + 1)
156+
const choiceIndex = parseInt(choice) - 1
157+
158+
if (choiceIndex >= 0 && choiceIndex < existingKeys.length) {
159+
// Use existing key - need to rotate to get actual value
160+
return await rotateExistingKey(
161+
apiKeyService,
162+
existingKeys[choiceIndex],
163+
options
164+
)
165+
} else if (choiceIndex === existingKeys.length) {
166+
// Create new key
167+
return await createNewKey(apiKeyService, projectName, options)
168+
}
169+
170+
console.log(chalk.red('Invalid selection.'))
171+
return null
172+
}
173+
174+
/**
175+
* Get user choice from stdin
176+
*/
177+
async function getUserChoice(maxOption: number): Promise<string> {
178+
return new Promise((resolve) => {
179+
const rl = readline.createInterface({
180+
input: process.stdin,
181+
output: process.stdout,
182+
})
183+
rl.question(
184+
chalk.blue(`\nSelect an option (1-${maxOption}): `),
185+
(answer) => {
186+
rl.close()
187+
resolve(answer.trim())
188+
}
189+
)
190+
})
191+
}
192+
193+
/**
194+
* Rotate an existing key to get the actual key value
195+
*/
196+
async function rotateExistingKey(
197+
apiKeyService: ApiKeyService,
198+
selectedKey: { id: number; name: string },
199+
options: CodeCommandOptions
200+
): Promise<ApiKeyResult | null> {
201+
console.log(
202+
chalk.yellow(
203+
`\n🔄 Rotating API key "${selectedKey.name}" to get the key value...`
204+
)
205+
)
206+
207+
if (
208+
await confirm(
209+
chalk.yellow('This will invalidate the current key. Continue? (Y/n): '),
210+
options.yes
211+
)
212+
) {
213+
const rotatedKey = await apiKeyService.rotate(selectedKey.id.toString())
214+
console.log(chalk.green('✓ API key rotated successfully'))
215+
return {
216+
apiKey: rotatedKey.key,
217+
keyName: selectedKey.name,
218+
}
219+
}
220+
221+
console.log(
222+
chalk.yellow(
223+
'Cancelled. Please select a different option or create a new key.'
224+
)
225+
)
226+
return null
227+
}
228+
229+
/**
230+
* Create a new API key
231+
*/
232+
async function createNewKey(
233+
apiKeyService: ApiKeyService,
234+
projectName: string,
235+
options: CodeCommandOptions
236+
): Promise<ApiKeyResult> {
237+
if (!options.yes) {
238+
console.log(chalk.yellow('No existing API keys found.'))
239+
}
240+
console.log(chalk.blue('Creating a new API key...'))
241+
242+
const defaultKeyName = `opencode-${projectName}-${Date.now()}`
243+
const customName = await getInput(
244+
chalk.blue(`Enter key name (default: ${defaultKeyName}): `),
245+
defaultKeyName,
246+
options.yes
247+
)
248+
249+
const createOptions: CreateApiKeyOptions = { name: customName }
250+
const keyData = await apiKeyService.create(createOptions)
251+
console.log(chalk.green(`✓ Created new API key: ${customName}`))
252+
253+
return {
254+
apiKey: keyData.key,
255+
keyName: customName,
256+
}
257+
}
258+
259+
/**
260+
* Print API key error and guidance
261+
*/
262+
function printApiKeyError(error: unknown): void {
263+
console.error(chalk.red('❌ Failed to handle API keys:'))
264+
console.log(chalk.blue('This could be due to:'))
265+
console.log(chalk.dim(' • Network connectivity issues'))
266+
console.log(chalk.dim(' • Invalid authentication credentials'))
267+
console.log(chalk.dim(' • API service temporarily unavailable'))
268+
console.log('')
269+
console.log(chalk.blue('Try using an API key directly:'))
270+
console.log(chalk.cyan(' export BERGET_API_KEY=your_api_key_here'))
271+
console.log(
272+
chalk.cyan(
273+
` berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT} --yes`
274+
)
275+
)
276+
handleError('API key operation failed', error)
277+
}

0 commit comments

Comments
 (0)