11import * as fs from "fs" ;
22import * as path from "path" ;
33
4- import { AssistantUnrolled , ModelConfig } from "@continuedev/config-yaml" ;
5- import { BaseLlmApi } from "@continuedev/openai-adapters" ;
6- import { DefaultApiInterface } from "@continuedev/sdk/dist/api/dist/index.js" ;
74import chalk from "chalk" ;
85
9- import { AuthConfig , isAuthenticated , login } from "./auth/workos.js" ;
10- import { initialize } from "./config.js" ;
6+ import { login } from "./auth/workos.js" ;
117import { env } from "./env.js" ;
12- import { processRule } from "./hubLoader.js" ;
13- import { MCPService } from "./services/MCPService.js" ;
148import {
159 getApiKeyValidationError ,
1610 isValidAnthropicApiKey ,
@@ -20,15 +14,6 @@ import { updateAnthropicModelInYaml } from "./util/yamlConfigUpdater.js";
2014
2115const CONFIG_PATH = path . join ( env . continueHome , "config.yaml" ) ;
2216
23- export interface OnboardingResult {
24- config : AssistantUnrolled ;
25- llmApi : BaseLlmApi ;
26- model : ModelConfig ;
27- mcpService : MCPService ;
28- apiClient : DefaultApiInterface ;
29- wasOnboarded : boolean ;
30- }
31-
3217export async function checkHasAcceptableModel (
3318 configPath : string ,
3419) : Promise < boolean > {
@@ -61,21 +46,18 @@ export async function createOrUpdateConfig(apiKey: string): Promise<void> {
6146
6247export async function runOnboardingFlow (
6348 configPath : string | undefined ,
64- authConfig : AuthConfig ,
65- ) : Promise < OnboardingResult > {
49+ ) : Promise < boolean > {
6650 // Step 1: Check if --config flag is provided
6751 if ( configPath !== undefined ) {
68- const result = await initialize ( authConfig , configPath ) ;
69- return { ...result , wasOnboarded : false } ;
52+ return false ;
7053 }
7154
7255 // Step 2: Check for CONTINUE_USE_BEDROCK environment variable first (before test env check)
7356 if ( process . env . CONTINUE_USE_BEDROCK === "1" ) {
7457 console . log (
7558 chalk . blue ( "✓ Using AWS Bedrock (CONTINUE_USE_BEDROCK detected)" ) ,
7659 ) ;
77- const result = await initialize ( authConfig , CONFIG_PATH ) ;
78- return { ...result , wasOnboarded : true } ;
60+ return true ;
7961 }
8062
8163 // Step 3: Check if we're in a test/CI environment - if so, skip interactive prompts
@@ -92,13 +74,11 @@ export async function runOnboardingFlow(
9274 console . log ( chalk . blue ( "✓ Using ANTHROPIC_API_KEY from environment" ) ) ;
9375 await createOrUpdateConfig ( process . env . ANTHROPIC_API_KEY ) ;
9476 console . log ( chalk . gray ( ` Config saved to: ${ CONFIG_PATH } ` ) ) ;
95- const result = await initialize ( authConfig , CONFIG_PATH ) ;
96- return { ...result , wasOnboarded : false } ;
77+ return false ;
9778 }
9879
9980 // Otherwise return a minimal working configuration
100- const result = await initialize ( authConfig , undefined ) ;
101- return { ...result , wasOnboarded : false } ;
81+ return false ;
10282 }
10383
10484 // Step 4: Present user with two options
@@ -114,13 +94,8 @@ export async function runOnboardingFlow(
11494 ) ;
11595
11696 if ( choice === "1" || choice === "" ) {
117- const newAuthConfig = await login ( ) ;
118-
119- const { ensureOrganization } = await import ( "./auth/workos.js" ) ;
120- const finalAuthConfig = await ensureOrganization ( newAuthConfig ) ;
121-
122- const result = await initialize ( finalAuthConfig , undefined ) ;
123- return { ...result , wasOnboarded : true } ;
97+ await login ( ) ;
98+ return true ;
12499 } else if ( choice === "2" ) {
125100 const apiKey = await question (
126101 chalk . white ( "\nEnter your Anthropic API key: " ) ,
@@ -135,89 +110,12 @@ export async function runOnboardingFlow(
135110 chalk . green ( `✓ Config file updated successfully at ${ CONFIG_PATH } ` ) ,
136111 ) ;
137112
138- const result = await initialize ( authConfig , CONFIG_PATH ) ;
139- return { ...result , wasOnboarded : true } ;
113+ return true ;
140114 } else {
141115 throw new Error ( `Invalid choice. Please select "1" or "2"` ) ;
142116 }
143117}
144118
145- export async function runNormalFlow (
146- authConfig : AuthConfig ,
147- configPath ?: string ,
148- rules ?: string [ ] ,
149- ) : Promise < OnboardingResult > {
150- // Step 1: Check if --config flag is provided
151- if ( configPath !== undefined ) {
152- // Empty string is invalid and should be treated as an error
153- if ( configPath === "" ) {
154- throw new Error (
155- `Failed to load config from "": Config path cannot be empty` ,
156- ) ;
157- }
158-
159- try {
160- const result = await initialize ( authConfig , configPath ) ;
161- // Inject rules into the config if provided
162- if ( rules && rules . length > 0 ) {
163- result . config = await injectRulesIntoConfig ( result . config , rules ) ;
164- }
165- return { ...result , wasOnboarded : false } ;
166- } catch ( error ) {
167- // If user explicitly provided --config flag, fail loudly instead of falling back
168- const errorMessage =
169- error instanceof Error ? error . message : String ( error ) ;
170- throw new Error (
171- `Failed to load config from "${ configPath } ": ${ errorMessage } ` ,
172- ) ;
173- }
174- }
175-
176- // Step 2: If user is logged in, look for first assistant in selected org
177- if ( isAuthenticated ( ) ) {
178- try {
179- const result = await initialize ( authConfig , undefined ) ;
180- // Inject rules into the config if provided
181- if ( rules && rules . length > 0 ) {
182- result . config = await injectRulesIntoConfig ( result . config , rules ) ;
183- }
184- return { ...result , wasOnboarded : false } ;
185- } catch {
186- // Silently ignore errors when loading default assistant
187- }
188- }
189-
190- // Step 3: Look for local ~/.continue/config.yaml
191- if ( fs . existsSync ( CONFIG_PATH ) ) {
192- try {
193- const result = await initialize ( authConfig , CONFIG_PATH ) ;
194- // Inject rules into the config if provided
195- if ( rules && rules . length > 0 ) {
196- result . config = await injectRulesIntoConfig ( result . config , rules ) ;
197- }
198- return { ...result , wasOnboarded : false } ;
199- } catch {
200- console . log ( chalk . yellow ( "⚠ Invalid config file found" ) ) ;
201- }
202- }
203-
204- // Step 4: Look for ANTHROPIC_API_KEY in environment
205- if ( process . env . ANTHROPIC_API_KEY ) {
206- console . log ( chalk . blue ( "✓ Using ANTHROPIC_API_KEY from environment" ) ) ;
207- await createOrUpdateConfig ( process . env . ANTHROPIC_API_KEY ) ;
208- console . log ( chalk . gray ( ` Config saved to: ${ CONFIG_PATH } ` ) ) ;
209- const result = await initialize ( authConfig , CONFIG_PATH ) ;
210- // Inject rules into the config if provided
211- if ( rules && rules . length > 0 ) {
212- result . config = await injectRulesIntoConfig ( result . config , rules ) ;
213- }
214- return { ...result , wasOnboarded : false } ;
215- }
216-
217- // Step 5: Fall back to onboarding flow
218- return runOnboardingFlow ( configPath , authConfig ) ;
219- }
220-
221119export async function isFirstTime ( ) : Promise < boolean > {
222120 return ! fs . existsSync ( path . join ( env . continueHome , ".onboarding_complete" ) ) ;
223121}
@@ -233,82 +131,13 @@ export async function markOnboardingComplete(): Promise<void> {
233131 fs . writeFileSync ( flagPath , new Date ( ) . toISOString ( ) ) ;
234132}
235133
236- /**
237- * Process rules and inject them into the assistant config
238- * @param config - The assistant config to modify
239- * @param rules - Array of rule specifications to process and inject
240- * @returns The modified config with injected rules
241- */
242- async function injectRulesIntoConfig (
243- config : AssistantUnrolled ,
244- rules : string [ ] ,
245- ) : Promise < AssistantUnrolled > {
246- if ( ! rules || rules . length === 0 ) {
247- return config ;
248- }
249-
250- const processedRules : string [ ] = [ ] ;
251- for ( const ruleSpec of rules ) {
252- try {
253- const processedRule = await processRule ( ruleSpec ) ;
254- processedRules . push ( processedRule ) ;
255- } catch ( error : any ) {
256- console . warn (
257- chalk . yellow (
258- `Warning: Failed to process rule "${ ruleSpec } ": ${ error . message } ` ,
259- ) ,
260- ) ;
261- }
262- }
263-
264- if ( processedRules . length === 0 ) {
265- return config ;
266- }
267-
268- // Clone the config to avoid mutating the original
269- const modifiedConfig = { ...config } ;
270-
271- // Add processed rules to the config's rules array
272- // Each processed rule is a string, which is a valid Rule type
273- const existingRules = modifiedConfig . rules || [ ] ;
274- modifiedConfig . rules = [ ...existingRules , ...processedRules ] ;
275-
276- return modifiedConfig ;
277- }
278-
279- export async function initializeWithOnboarding (
280- authConfig : AuthConfig ,
281- configPath : string | undefined ,
282- rules ?: string [ ] ,
283- ) {
134+ export async function initializeWithOnboarding ( configPath : string | undefined ) {
284135 const firstTime = await isFirstTime ( ) ;
285136
286- if ( firstTime ) {
287- const onboardingResult = await runOnboardingFlow ( configPath , authConfig ) ;
288- if ( onboardingResult . wasOnboarded ) {
289- await markOnboardingComplete ( ) ;
290- }
291- // Inject rules into the config if provided (for onboarding flow which doesn't handle rules directly)
292- if ( rules && rules . length > 0 && ! onboardingResult . wasOnboarded ) {
293- onboardingResult . config = await injectRulesIntoConfig (
294- onboardingResult . config ,
295- rules ,
296- ) ;
297- }
298- } else {
299- // when running normal flow, initialize (remote) config asynchronously
300- void ( async ( ) => {
301- const onboardingResult = await runNormalFlow (
302- authConfig ,
303- configPath ,
304- rules ,
305- ) ;
306- if ( rules && rules . length > 0 ) {
307- onboardingResult . config = await injectRulesIntoConfig (
308- onboardingResult . config ,
309- rules ,
310- ) ;
311- }
312- } ) ( ) ;
137+ if ( ! firstTime ) return ;
138+
139+ const wasOnboarded = await runOnboardingFlow ( configPath ) ;
140+ if ( wasOnboarded ) {
141+ await markOnboardingComplete ( ) ;
313142 }
314143}
0 commit comments