@@ -5,7 +5,7 @@ import ora from 'ora';
55import * as path from 'path' ;
66import { OktaAPIClient } from './lib/okta-api.js' ;
77import { generateRSAKeyPair , savePrivateKey } from './lib/key-generator.js' ;
8- import { generateAgent0AppEnv , generateAgent0AgentEnv , generateTodo0AppEnv , generateTodo0McpEnv , writeEnvFile , writeConfigReport , } from './lib/env-writer.js' ;
8+ import { generateAgent0AppEnv , generateAgent0AgentEnv , generateTodo0AppEnv , generateTodo0McpEnv , generateOpaEnvStub , writeEnvFile , writeConfigReport , } from './lib/env-writer.js' ;
99import { loadRollbackState , updateRollbackState , } from './lib/state-manager.js' ;
1010import { AgentIdentityAPIClient , convertPublicKeyToJWK , constructAuthServerORN , } from './lib/agent-identity-api.js' ;
1111/**
@@ -300,9 +300,9 @@ async function bootstrap() {
300300 const connection = await agentClient . createConnection ( agentIdentityId , {
301301 connectionType : 'IDENTITY_ASSERTION_CUSTOM_AS' ,
302302 authorizationServer : {
303- orn : authServerOrn ,
304- resourceIndicator : config . mcpAudience ,
303+ orn : authServerOrn
305304 } ,
305+ resourceIndicator : config . mcpAudience ,
306306 scopeCondition : 'INCLUDE_ONLY' ,
307307 scopes : mcpScopes ,
308308 } ) ;
@@ -378,8 +378,126 @@ async function bootstrap() {
378378 else {
379379 spinner . succeed ( `Trusted origins configured (${ createdOrigins . length } created, ${ origins . length - createdOrigins . length } already existed)` ) ;
380380 }
381- // Step 13: Generate Configuration Files
382- console . log ( chalk . bold ( '\n📋 Step 13: Generating Configuration Files' ) ) ;
381+ // Step 13: Configure LLM Credentials
382+ console . log ( chalk . bold ( '\n📋 Step 13: Configure LLM Credentials' ) ) ;
383+ const llmModeAnswer = await prompts ( {
384+ type : 'select' ,
385+ name : 'mode' ,
386+ message : 'How would you like to configure LLM credentials?' ,
387+ choices : [
388+ {
389+ title : 'Environment Variables (Simple)' ,
390+ value : 'env' ,
391+ description : 'Store API key in .env.agent file' ,
392+ } ,
393+ {
394+ title : 'Okta Privileged Access (Secure)' ,
395+ value : 'opa' ,
396+ description : 'Fetch credentials from OPA vault per user session' ,
397+ } ,
398+ {
399+ title : 'Skip (Manual Setup)' ,
400+ value : 'skip' ,
401+ description : 'Configure .env.agent or .env.opa later' ,
402+ } ,
403+ ] ,
404+ initial : 0 ,
405+ } ) ;
406+ let llmConfig = { provider : 'skip' } ;
407+ if ( llmModeAnswer . mode === 'env' ) {
408+ // Direct environment variable mode
409+ const providerAnswer = await prompts ( {
410+ type : 'select' ,
411+ name : 'provider' ,
412+ message : 'Which LLM provider?' ,
413+ choices : [
414+ { title : 'Anthropic (Claude)' , value : 'anthropic' } ,
415+ { title : 'AWS Bedrock' , value : 'bedrock' } ,
416+ ] ,
417+ } ) ;
418+ if ( providerAnswer . provider === 'anthropic' ) {
419+ const anthropicAnswers = await prompts ( [
420+ {
421+ type : 'password' ,
422+ name : 'apiKey' ,
423+ message : 'Anthropic API Key:' ,
424+ validate : ( v ) => ( v && v . startsWith ( 'sk-ant-' ) ) || 'API key must start with sk-ant-' ,
425+ } ,
426+ {
427+ type : 'text' ,
428+ name : 'model' ,
429+ message : 'Model ID:' ,
430+ initial : 'claude-sonnet-4-20250514' ,
431+ } ,
432+ ] ) ;
433+ llmConfig = {
434+ provider : 'anthropic' ,
435+ apiKey : anthropicAnswers . apiKey ,
436+ model : anthropicAnswers . model ,
437+ } ;
438+ }
439+ else if ( providerAnswer . provider === 'bedrock' ) {
440+ const bedrockAnswers = await prompts ( [
441+ {
442+ type : 'text' ,
443+ name : 'region' ,
444+ message : 'AWS Region:' ,
445+ initial : 'us-east-1' ,
446+ } ,
447+ {
448+ type : 'password' ,
449+ name : 'accessKeyId' ,
450+ message : 'AWS Access Key ID:' ,
451+ validate : ( v ) => ! ! v || 'Required' ,
452+ } ,
453+ {
454+ type : 'password' ,
455+ name : 'secretAccessKey' ,
456+ message : 'AWS Secret Access Key:' ,
457+ validate : ( v ) => ! ! v || 'Required' ,
458+ } ,
459+ {
460+ type : 'password' ,
461+ name : 'sessionToken' ,
462+ message : 'AWS Session Token (optional, press Enter to skip):' ,
463+ } ,
464+ {
465+ type : 'text' ,
466+ name : 'modelId' ,
467+ message : 'Bedrock Model ID:' ,
468+ initial : 'anthropic.claude-3-sonnet-20240229-v1:0' ,
469+ } ,
470+ ] ) ;
471+ llmConfig = {
472+ provider : 'bedrock' ,
473+ region : bedrockAnswers . region ,
474+ accessKeyId : bedrockAnswers . accessKeyId ,
475+ secretAccessKey : bedrockAnswers . secretAccessKey ,
476+ sessionToken : bedrockAnswers . sessionToken || undefined ,
477+ modelId : bedrockAnswers . modelId ,
478+ } ;
479+ }
480+ }
481+ else if ( llmModeAnswer . mode === 'opa' ) {
482+ // OPA mode - ask which provider they'll use
483+ const opaProviderAnswer = await prompts ( {
484+ type : 'select' ,
485+ name : 'llmProvider' ,
486+ message : 'Which LLM provider will you store in OPA?' ,
487+ choices : [
488+ { title : 'Anthropic (Claude)' , value : 'anthropic' } ,
489+ { title : 'AWS Bedrock' , value : 'bedrock' } ,
490+ ] ,
491+ } ) ;
492+ llmConfig = {
493+ provider : 'opa' ,
494+ llmProvider : opaProviderAnswer . llmProvider ,
495+ } ;
496+ }
497+ // Store LLM config for env generation
498+ bootstrapConfig . llmConfig = llmConfig ;
499+ // Step 14: Generate Configuration Files
500+ console . log ( chalk . bold ( '\n📋 Step 14: Generating Configuration Files' ) ) ;
383501 spinner = ora ( 'Writing .env files...' ) . start ( ) ;
384502 const agent0AppEnv = generateAgent0AppEnv ( bootstrapConfig ) ;
385503 writeEnvFile ( 'packages/agent0/.env.app' , agent0AppEnv ) ;
@@ -389,16 +507,35 @@ async function bootstrap() {
389507 writeEnvFile ( 'packages/todo0/.env.app' , todo0AppEnv ) ;
390508 const todo0McpEnv = generateTodo0McpEnv ( bootstrapConfig ) ;
391509 writeEnvFile ( 'packages/todo0/.env.mcp' , todo0McpEnv ) ;
510+ // Generate .env.opa stub if OPA mode selected
511+ if ( llmConfig . provider === 'opa' ) {
512+ const opaEnvStub = generateOpaEnvStub ( llmConfig . llmProvider ) ;
513+ writeEnvFile ( 'packages/agent0/.env.opa' , opaEnvStub ) ;
514+ }
392515 writeConfigReport ( bootstrapConfig ) ;
393516 spinner . succeed ( 'Configuration files generated' ) ;
394517 // Success!
395518 console . log ( chalk . bold . green ( '\n✅ Bootstrap Complete!\n' ) ) ;
396- console . log ( 'Next steps:' ) ;
397- console . log ( ` 1. ${ chalk . cyan ( 'pnpm install' ) } - Install dependencies` ) ;
398- console . log ( ` 2. ${ chalk . cyan ( 'pnpm run bootstrap' ) } - Bootstrap database` ) ;
399- console . log ( ` 3. ${ chalk . cyan ( 'pnpm run start:todo0' ) } - Start REST API` ) ;
400- console . log ( ` 4. ${ chalk . cyan ( 'pnpm run start:mcp' ) } - Start MCP Server` ) ;
401- console . log ( ` 5. ${ chalk . cyan ( 'pnpm run start:agent0' ) } - Start Agent` ) ;
519+ if ( llmConfig . provider === 'opa' ) {
520+ // OPA mode - show OPA-specific next steps
521+ console . log ( chalk . bold ( 'Next steps for OPA mode:\n' ) ) ;
522+ console . log ( ` ${ chalk . cyan ( '1.' ) } ${ chalk . white ( 'pnpm install' ) } - Install dependencies` ) ;
523+ console . log ( ` ${ chalk . cyan ( '2.' ) } ${ chalk . white ( 'pnpm run bootstrap' ) } - Bootstrap database` ) ;
524+ console . log ( ` ${ chalk . cyan ( '3.' ) } ${ chalk . yellow ( 'pnpm run setup:opa' ) } - Create secrets in OPA vault` ) ;
525+ console . log ( ` ${ chalk . cyan ( '4.' ) } ${ chalk . yellow ( 'pnpm run link:opa' ) } - Connect agent to OPA secrets` ) ;
526+ console . log ( ` ${ chalk . cyan ( '5.' ) } ${ chalk . white ( 'pnpm run dev' ) } - Start all services` ) ;
527+ console . log ( chalk . gray ( '\n Note: Steps 3-4 configure OPA secret management' ) ) ;
528+ }
529+ else {
530+ // Direct mode or skip - show standard next steps
531+ console . log ( 'Next steps:' ) ;
532+ console . log ( ` 1. ${ chalk . cyan ( 'pnpm install' ) } - Install dependencies` ) ;
533+ console . log ( ` 2. ${ chalk . cyan ( 'pnpm run bootstrap' ) } - Bootstrap database` ) ;
534+ console . log ( ` 3. ${ chalk . cyan ( 'pnpm run dev' ) } - Start all services` ) ;
535+ if ( llmConfig . provider === 'skip' ) {
536+ console . log ( chalk . yellow ( '\n ⚠️ Remember to configure LLM credentials in .env.agent' ) ) ;
537+ }
538+ }
402539 console . log ( `\n Optional: ${ chalk . cyan ( 'pnpm run validate:okta' ) } - Validate configuration` ) ;
403540 console . log ( `\n📄 See ${ chalk . cyan ( 'okta-config-report.md' ) } for detailed configuration\n` ) ;
404541 }
0 commit comments