diff --git a/cmd/agent.go b/cmd/agent.go index 38e53395..44c5f0b1 100644 --- a/cmd/agent.go +++ b/cmd/agent.go @@ -165,14 +165,15 @@ var agentDeleteCmd = &cobra.Command{ func getAgentAuthType(logger logger.Logger, authType string) string { if authType != "" { switch authType { - case "bearer", "none": + case "project", "bearer", "none": return authType default: } } auth := tui.Select(logger, "Select your Agent's webhook authentication method", "Do you want to secure the webhook or make it publicly available?", []tui.Option{ - {Text: tui.PadRight("API Key", 10, " ") + tui.Muted("Bearer Token (will be generated for you)"), ID: "bearer"}, - {Text: tui.PadRight("None", 10, " ") + tui.Muted("No Authentication Required"), ID: "none"}, + {Text: tui.PadRight("API Key", 20, " ") + tui.Muted("Bearer Token (will be generated for you)"), ID: "bearer"}, + {Text: tui.PadRight("Project API Key", 20, " ") + tui.Muted("The Project Key attched to your project"), ID: "project"}, + {Text: tui.PadRight("None", 20, " ") + tui.Muted("No Authentication Required"), ID: "none"}, }) return auth } diff --git a/cmd/cloud.go b/cmd/cloud.go index 02df3077..b142de93 100644 --- a/cmd/cloud.go +++ b/cmd/cloud.go @@ -113,7 +113,7 @@ func ShowNewProjectImport(ctx context.Context, logger logger.Logger, cmd *cobra. errsystem.New(errsystem.ErrSaveProject, err, errsystem.WithContextMessage("Error saving project after import")).ShowErrorAndExit() } - saveEnv(dir, result.APIKey) + saveEnv(dir, result.APIKey, result.ProjectKey) }) tui.ShowSuccess("Project imported successfully") } diff --git a/cmd/project.go b/cmd/project.go index b1ccd135..dd1c23c8 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -36,22 +36,50 @@ Use the subcommands to manage your projects.`, }, } -func saveEnv(dir string, apikey string) { +func saveEnv(dir string, apikey string, projectKey string) { filename := filepath.Join(dir, ".env") envLines, err := env.ParseEnvFile(filename) if err != nil { errsystem.New(errsystem.ErrReadConfigurationFile, err, errsystem.WithContextMessage("Failed to parse .env file")).ShowErrorAndExit() } - var found bool + found := map[string]bool{ + "AGENTUITY_SDK_KEY": false, + "AGENTUITY_API_KEY": false, + "AGENTUITY_PROJECT_KEY": false, + } + for i, envLine := range envLines { if envLine.Key == "AGENTUITY_API_KEY" { envLines[i].Val = apikey - found = true + found["AGENTUITY_API_KEY"] = true + } + if envLine.Key == "AGENTUITY_SDK_KEY" { + envLines[i].Val = apikey + found[envLine.Key] = true + } + if envLine.Key == "AGENTUITY_PROJECT_KEY" { + envLines[i].Val = projectKey + found[envLine.Key] = true + } + } + + if found["AGENTUITY_API_KEY"] { + // Remove AGENTUITY_API_KEY since it's deprecated in newer SDK versions + for i := len(envLines) - 1; i >= 0; i-- { + if envLines[i].Key == "AGENTUITY_API_KEY" { + envLines = append(envLines[:i], envLines[i+1:]...) + } } + found["AGENTUITY_API_KEY"] = false } - if !found { - envLines = append(envLines, env.EnvLine{Key: "AGENTUITY_API_KEY", Val: apikey}) + + if !found["AGENTUITY_SDK_KEY"] { + envLines = append(envLines, env.EnvLine{Key: "AGENTUITY_SDK_KEY", Val: apikey}) + } + if !found["AGENTUITY_PROJECT_KEY"] { + envLines = append(envLines, env.EnvLine{Key: "AGENTUITY_PROJECT_KEY", Val: projectKey}) } + if err := env.WriteEnvFile(filename, envLines); err != nil { errsystem.New(errsystem.ErrWriteConfigurationFile, err, errsystem.WithContextMessage("Failed to write .env file")).ShowErrorAndExit() } @@ -65,6 +93,7 @@ type InitProjectArgs struct { Name string Description string EnableWebhookAuth bool + AuthType string Provider *templates.TemplateRules Agents []project.AgentConfig } @@ -78,6 +107,7 @@ func initProject(ctx context.Context, logger logger.Logger, args InitProjectArgs Name: args.Name, Description: args.Description, EnableWebhookAuth: args.EnableWebhookAuth, + AuthType: args.AuthType, Dir: args.Dir, Provider: args.Provider.Identifier, Agents: args.Agents, @@ -126,7 +156,7 @@ func initProject(ctx context.Context, logger logger.Logger, args InitProjectArgs errsystem.New(errsystem.ErrSaveProject, err, errsystem.WithContextMessage("Failed to save project to disk")).ShowErrorAndExit() } - saveEnv(args.Dir, result.APIKey) + saveEnv(args.Dir, result.APIKey, result.ProjectKey) return result } @@ -540,7 +570,8 @@ Examples: Description: description, Provider: rules, Agents: agents, - EnableWebhookAuth: authType != "none", + EnableWebhookAuth: authType == "project" || authType == "webhook", + AuthType: authType, }) // remember our choices @@ -562,7 +593,7 @@ Examples: para = append(para, tui.Secondary("1. Switch into the project directory at ")+tui.Directory(projectDir)) para = append(para, tui.Secondary("2. Run ")+tui.Command("dev")+tui.Secondary(" to run the project locally in development mode")) para = append(para, tui.Secondary("3. Run ")+tui.Command("deploy")+tui.Secondary(" to deploy the project to the Agentuity Agent Cloud")) - if authType != "none" { + if authType == "project" || authType == "webhook" { para = append(para, tui.Secondary("4. Run ")+tui.Command("agent apikey")+tui.Secondary(" to fetch the Webhook API key for the agent")) } para = append(para, tui.Secondary("🏠 Access your project at ")+tui.Link("%s/projects/%s", appUrl, projectData.ProjectId)) @@ -811,6 +842,6 @@ func init() { projectNewCmd.Flags().StringP("template", "t", "", "The template to use for the project") projectNewCmd.Flags().Bool("force", false, "Force the project to be created even if the directory already exists") projectNewCmd.Flags().String("templates-dir", "", "The directory to load the templates. Defaults to loading them from the github.com/agentuity/templates repository") - projectNewCmd.Flags().String("auth", "bearer", "The authentication type for the agent (bearer or none)") + projectNewCmd.Flags().String("auth", "project", "The authentication type for the agent (project, webhook, or none)") projectNewCmd.Flags().String("action", "github-app", "The action to take for the project (github-action, github-app, none)") } diff --git a/internal/bundler/anthropic.go b/internal/bundler/anthropic.go index a3d08a0f..111c13cb 100644 --- a/internal/bundler/anthropic.go +++ b/internal/bundler/anthropic.go @@ -5,7 +5,7 @@ func init() { Module: "@anthropic-ai", Filename: "sdk\\/index.*", Body: &patchAction{ - Before: generateEnvGuard("ANTHROPIC_API_KEY", generateGatewayEnvGuard("ANTHROPIC_API_KEY", "process.env.AGENTUITY_API_KEY", "ANTHROPIC_BASE_URL", "anthropic")), + Before: generateEnvGuard("ANTHROPIC_API_KEY", generateGatewayEnvGuard("ANTHROPIC_API_KEY", "process.env.AGENTUITY_SDK_KEY", "ANTHROPIC_BASE_URL", "anthropic")), }, } } diff --git a/internal/bundler/openai.go b/internal/bundler/openai.go index c3064089..e0e1fde3 100644 --- a/internal/bundler/openai.go +++ b/internal/bundler/openai.go @@ -5,7 +5,7 @@ func init() { Module: "openai", Filename: "index", Body: &patchAction{ - Before: generateEnvGuard("OPENAI_API_KEY", generateGatewayEnvGuard("OPENAI_API_KEY", "process.env.AGENTUITY_API_KEY", "OPENAI_BASE_URL", "openai")), + Before: generateEnvGuard("OPENAI_API_KEY", generateGatewayEnvGuard("OPENAI_API_KEY", "process.env.AGENTUITY_SDK_KEY", "OPENAI_BASE_URL", "openai")), }, } } diff --git a/internal/bundler/patch.go b/internal/bundler/patch.go index 3422210c..a1a3e1ce 100644 --- a/internal/bundler/patch.go +++ b/internal/bundler/patch.go @@ -37,14 +37,14 @@ _args = _newargs;`, index, inject) } func generateEnvGuard(name string, inject string) string { - return fmt.Sprintf(`if (!process.env.%[1]s || process.env.%[1]s === process.env.AGENTUITY_API_KEY) { + return fmt.Sprintf(`if (!process.env.%[1]s || process.env.%[1]s === process.env.AGENTUITY_SDK_KEY) { %[2]s }`, name, inject) } func generateGatewayEnvGuard(apikey string, apikeyval string, apibase string, provider string) string { return fmt.Sprintf(`{ - const apikey = process.env.AGENTUITY_API_KEY; + const apikey = process.env.AGENTUITY_SDK_KEY; const url = process.env.AGENTUITY_TRANSPORT_URL; if (url && apikey) { process.env.%[1]s = %[2]s; diff --git a/internal/bundler/upgrade.go b/internal/bundler/upgrade.go index db13035a..78f0d424 100644 --- a/internal/bundler/upgrade.go +++ b/internal/bundler/upgrade.go @@ -14,10 +14,11 @@ import ( ) type breakingChange struct { - Title string - Message string - Runtime string - Version string + Title string + Message string + Runtime string + Version string + Callback func(ctx BundleContext) error } var breakingChanges = []breakingChange{ @@ -39,6 +40,96 @@ var breakingChanges = []breakingChange{ Title: "🚫 Python SDK Breaking Changes 🚫", Message: "The Python SDK type signatures for AgentRequest have changed to be async functions. Please see the v0.0.82 Changelog for how to update your code.\n\n" + tui.Link("https://agentuity.dev/Changelog/sdk-py#v0082") + "\n\nPlease run `uv add agentuity -U` fix your types and ensure your code passes type checking and then re-run this command again.", }, + { + Runtime: "bunjs", + Version: "<0.0.115", + Title: "🚫 JS SDK Breaking Change 🚫", + Message: "The environment variable and code reference for your Agentuity API key has changed from AGENTUITY_API_KEY to AGENTUITY_SDK_KEY. Update all occurrences in your .env files and codebase. See the v0.0.115 Changelog for details.\n\n" + tui.Link("https://agentuity.dev/Changelog/sdk-js#v00115") + "\n\nAfter migrated, please run bun update @agentuity/sdk --latest and then re-run this command again.", + Callback: func(ctx BundleContext) error { + files := []string{ + filepath.Join(ctx.ProjectDir, "index.ts"), + filepath.Join(ctx.ProjectDir, ".env"), + } + for _, file := range files { + if !util.Exists(file) { + continue + } + content, err := os.ReadFile(file) + if err != nil { + return err + } + updated := strings.ReplaceAll(string(content), `AGENTUITY_API_KEY`, `AGENTUITY_SDK_KEY`) + if updated != string(content) { + err = os.WriteFile(file, []byte(updated), 0644) + if err != nil { + return err + } + } + } + + return nil + }, + }, + { + Runtime: "nodejs", + Version: "<0.0.115 ", + Title: "🚫 JS SDK Breaking Change 🚫", + Message: "The environment variable and code reference for your Agentuity API key has changed from AGENTUITY_API_KEY to AGENTUITY_SDK_KEY. Update all occurrences in your .env files and codebase. See the v0.0.115 Changelog for details.\n\n" + tui.Link("https://agentuity.dev/Changelog/sdk-js#v00115") + "\n\nAfter migrated, please run npm i @agentuity/sdk --latest and then re-run this command again.", + Callback: func(ctx BundleContext) error { + files := []string{ + filepath.Join(ctx.ProjectDir, "index.ts"), + filepath.Join(ctx.ProjectDir, ".env"), + } + for _, file := range files { + if !util.Exists(file) { + continue + } + content, err := os.ReadFile(file) + if err != nil { + return err + } + updated := strings.ReplaceAll(string(content), `AGENTUITY_API_KEY`, `AGENTUITY_SDK_KEY`) + if updated != string(content) { + err = os.WriteFile(file, []byte(updated), 0644) + if err != nil { + return err + } + } + } + + return nil + }, + }, + { + Runtime: "uv", + Version: "<0.0.84", + Title: "🚫 Python SDK Breaking Changes 🚫", + Message: "The environment variable and code reference for your Agentuity API key has changed from AGENTUITY_API_KEY to AGENTUITY_SDK_KEY. Update all occurrences in your .env files and codebase. See the v0.0.84 Changelog for details.\n\n" + tui.Link("https://agentuity.dev/Changelog/python-js#v0084") + "\n\nAfter migrated, please run `uv add agentuity -U` --latest and then re-run this command again.", + Callback: func(ctx BundleContext) error { + files := []string{ + filepath.Join(ctx.ProjectDir, "server.py"), + filepath.Join(ctx.ProjectDir, ".env"), + } + for _, file := range files { + if !util.Exists(file) { + continue + } + content, err := os.ReadFile(file) + if err != nil { + return err + } + updated := strings.ReplaceAll(string(content), `AGENTUITY_API_KEY`, `AGENTUITY_SDK_KEY`) + if updated != string(content) { + err = os.WriteFile(file, []byte(updated), 0644) + if err != nil { + return err + } + } + } + + return nil + }, + }, } type packageJSON struct { @@ -54,80 +145,102 @@ type UVLockfile struct { Packages []UVPackage `toml:"package"` } -func checkForBreakingChanges(ctx BundleContext, language string, runtime string) error { - ctx.Logger.Trace("Checking for breaking changes in %s, runtime: %s", language, runtime) +func getSDKVersionJavascript(ctx BundleContext) (*semver.Version, error) { + var pkg packageJSON + pkgjson := filepath.Join(ctx.ProjectDir, "node_modules", "@agentuity", "sdk", "package.json") + if !util.Exists(pkgjson) { + return nil, fmt.Errorf("package.json not found: %s", pkgjson) + } + content, err := os.ReadFile(pkgjson) + if err != nil { + return nil, err + } + if err := json.Unmarshal(content, &pkg); err != nil { + return nil, err + } + currentVersion := semver.MustParse(pkg.Version) + return currentVersion, nil +} + +func getSDKVersionPython(ctx BundleContext) (*semver.Version, error) { + uvlock := filepath.Join(ctx.ProjectDir, "uv.lock") + if !util.Exists(uvlock) { + return nil, fmt.Errorf("uv.lock not found: %s", uvlock) + } + file, err := os.Open(uvlock) + if err != nil { + return nil, err + } + defer file.Close() + var lockfile UVLockfile + if err := toml.NewDecoder(file).Decode(&lockfile); err != nil { + return nil, err + } + for _, pkg := range lockfile.Packages { + if pkg.Name == "agentuity" && !strings.Contains(pkg.Version, "+") { + currentVersion := semver.MustParse(pkg.Version) + return currentVersion, nil + } + } + return nil, fmt.Errorf("agentuity package not found in uv.lock") +} + +func GetSDKVersion(language string, ctx BundleContext) (*semver.Version, error) { switch language { case "python": - uvlock := filepath.Join(ctx.ProjectDir, "uv.lock") - ctx.Logger.Trace("Checking for breaking changes in %s, exists: %t", uvlock, util.Exists(uvlock)) - if util.Exists(uvlock) { - var lockfile UVLockfile - content, err := os.Open(uvlock) - if err != nil { - return err - } - if err := toml.NewDecoder(content).Decode(&lockfile); err != nil { - return err - } - for _, pkg := range lockfile.Packages { - ctx.Logger.Trace("Checking for breaking changes in %s", pkg.Name) - if pkg.Name == "agentuity" && !strings.Contains(pkg.Version, "+") { - currentVersion := semver.MustParse(pkg.Version) - for _, change := range breakingChanges { - if change.Runtime != runtime { - continue - } - c, err := semver.NewConstraint(change.Version) - if err != nil { - return fmt.Errorf("error parsing semver constraint %s: %w", change.Version, err) - } - if c.Check(currentVersion) { - if tui.HasTTY { - tui.ShowBanner(change.Title, change.Message, true) - os.Exit(1) - } else { - ctx.Logger.Fatal(change.Message) - } - } - } - } - } - } + return getSDKVersionPython(ctx) case "javascript": - pkgjson := filepath.Join(ctx.ProjectDir, "node_modules", "@agentuity", "sdk", "package.json") - if util.Exists(pkgjson) { - var pkg packageJSON - content, err := os.ReadFile(pkgjson) - if err != nil { - return err - } - if err := json.Unmarshal(content, &pkg); err != nil { - return err - } - if strings.Contains(pkg.Version, "-pre") { - return nil - } - currentVersion := semver.MustParse(pkg.Version) - for _, change := range breakingChanges { - if change.Runtime != runtime { - continue - } - c, err := semver.NewConstraint(change.Version) - if err != nil { - return fmt.Errorf("error parsing semver constraint %s: %w", change.Version, err) + return getSDKVersionJavascript(ctx) + default: + return nil, fmt.Errorf("unsupported language: %s", language) + } +} + +func checkForBreakingChanges(ctx BundleContext, language string, runtime string) error { + ctx.Logger.Trace("Checking for breaking changes in %s, runtime: %s", language, runtime) + currentVersion, err := GetSDKVersion(language, ctx) + if err != nil { + return err + } + for _, change := range breakingChanges { + if change.Runtime != runtime { + continue + } + c, err := semver.NewConstraint(change.Version) + if err != nil { + return fmt.Errorf("error parsing semver constraint %s: %w", change.Version, err) + } + if strings.Contains(currentVersion.String(), "-pre") { + return nil + } + if c.Check(currentVersion) { + if change.Callback != nil { + var proceed bool + if tui.HasTTY { + tui.ShowBanner(change.Title, change.Message, true) + } else { + tui.Text(change.Title + " - " + change.Message) + return fmt.Errorf("migration required") } - if c.Check(currentVersion) { - if tui.HasTTY { - tui.ShowBanner(change.Title, change.Message, true) - os.Exit(1) - } else { - ctx.Logger.Fatal(change.Message) + proceed = tui.AskForConfirm("Would you like to migrate your project now?", 'y') == 'y' + if proceed { + if err := change.Callback(ctx); err != nil { + return err } + os.Exit(1) + } else { + return fmt.Errorf("migration required") + } + } else { + if tui.HasTTY { + tui.ShowBanner(change.Title, change.Message, true) + os.Exit(1) + } else { + ctx.Logger.Fatal(change.Message) } } } - default: - return nil } + return nil } diff --git a/internal/bundler/vercel_ai.go b/internal/bundler/vercel_ai.go index 66c3cacb..3fbea7c1 100644 --- a/internal/bundler/vercel_ai.go +++ b/internal/bundler/vercel_ai.go @@ -5,7 +5,7 @@ import "fmt" func generateVercelAIProvider(name string, envkey string) string { return generateJSArgsPatch(0, "") + fmt.Sprintf(`const opts = {...(_args[0] ?? {}) }; if (!opts.baseURL) { - const apikey = process.env.AGENTUITY_API_KEY; + const apikey = process.env.AGENTUITY_SDK_KEY; const url = process.env.AGENTUITY_TRANSPORT_URL; if (url && apikey) { opts.apiKey = apikey; diff --git a/internal/project/project.go b/internal/project/project.go index 7fa67eff..2d285777 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -43,6 +43,7 @@ type initProjectResult struct { type ProjectData struct { APIKey string `json:"api_key"` + ProjectKey string `json:"projectKey"` ProjectId string `json:"id"` OrgId string `json:"orgId"` Env map[string]string `json:"env"` @@ -61,6 +62,7 @@ type InitProjectArgs struct { Description string EnableWebhookAuth bool Agents []AgentConfig + AuthType string } // InitProject will create a new project in the organization. @@ -75,12 +77,14 @@ func InitProject(ctx context.Context, logger logger.Logger, args InitProjectArgs }) } payload := map[string]any{ - "organization_id": args.OrgId, - "provider": args.Provider, - "name": args.Name, + "organization_id": args.OrgId, + "provider": args.Provider, + "name": args.Name, + "description": args.Description, "enableWebhookAuth": args.EnableWebhookAuth, "agents": agents, + "authType": args.AuthType, } logger.Trace("sending new project payload: %s", cstr.JSONStringify(payload)) @@ -90,7 +94,6 @@ func InitProject(ctx context.Context, logger logger.Logger, args InitProjectArgs if err := client.Do("POST", initPath, payload, &result); err != nil { return nil, err } - return &result.Data, nil } @@ -376,6 +379,7 @@ type ProjectImportResponse struct { ID string `json:"id"` Agents []AgentConfig `json:"agents"` APIKey string `json:"apiKey"` + ProjectKey string `json:"projectKey"` IOAuthToken string `json:"ioAuthToken"` } diff --git a/internal/ui/project.go b/internal/ui/project.go index e3507a90..e8d2f42c 100644 --- a/internal/ui/project.go +++ b/internal/ui/project.go @@ -305,8 +305,12 @@ func initialProjectModel(initialForm ProjectForm) projectFormModel { } if initialForm.AgentAuthType != "" { agentAuthType = initialForm.AgentAuthType - if agentAuthType == "apikey" { + if agentAuthType == "none" { + authCursor = 0 + } else if agentAuthType == "project" { authCursor = 1 + } else if agentAuthType == "bearer" { + authCursor = 2 } } @@ -583,10 +587,15 @@ func (m projectFormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "left", "esc": if m.step == 3 && !m.agentName.Focused() && !m.agentDesc.Focused() { - if m.authCursor == 1 { - // When on auth options and API Key is selected, focus None - m.authCursor = 0 - m.agentAuthType = "none" + if m.authCursor > 0 { + m.authCursor-- + if m.authCursor == 0 { + m.agentAuthType = "none" + } else if m.authCursor == 1 { + m.agentAuthType = "project" + } else if m.authCursor == 2 { + m.agentAuthType = "agent" + } break } } @@ -635,9 +644,17 @@ func (m projectFormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "right": if m.step == 3 && !m.agentName.Focused() && !m.agentDesc.Focused() { - // Toggle between None and API Key - m.authCursor = 1 - m.agentAuthType = "apikey" + // Toggle between None, Project API Key, and Agent API Key + if m.authCursor < 2 { + m.authCursor++ + if m.authCursor == 0 { + m.agentAuthType = "none" + } else if m.authCursor == 1 { + m.agentAuthType = "project" + } else if m.authCursor == 2 { + m.agentAuthType = "agent" + } + } } else { // Only advance if current step is valid if m.step == 0 && m.cursor < len(m.choices) { @@ -829,8 +846,10 @@ func (m projectFormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // When on auth options, confirm selection and move to deployment step if m.authCursor == 0 { m.agentAuthType = "none" - } else { - m.agentAuthType = "apikey" + } else if m.authCursor == 1 { + m.agentAuthType = "project" + } else if m.authCursor == 2 { + m.agentAuthType = "agent" } m.step++ m.cursor = m.stepCursors[m.step] @@ -1260,15 +1279,19 @@ func (m projectFormModel) View() string { content.WriteString(selectedItemStyle.Render("Authentication") + "\n") if !m.agentName.Focused() && !m.agentDesc.Focused() { if m.authCursor == 0 { - content.WriteString(selectedItemStyle.UnsetForeground().Render("> [•] None") + " " + itemStyle.UnsetForeground().Render(" [ ] API Key") + "\n") - } else { - content.WriteString(itemStyle.UnsetForeground().Render(" [ ] None") + " " + selectedItemStyle.UnsetForeground().Render("> [•] API Key") + "\n") + content.WriteString(itemStyle.UnsetForeground().Render(" [•] None [ ] Project API Key [ ] Agent API Key\n")) + } else if m.authCursor == 1 { + content.WriteString(itemStyle.UnsetForeground().Render(" [ ] None [•] Project API Key [ ] Agent API Key\n")) + } else if m.authCursor == 2 { + content.WriteString(itemStyle.UnsetForeground().Render(" [ ] None [ ] Project API Key [•] Agent API Key\n")) } } else { if m.authCursor == 0 { - content.WriteString(itemStyle.UnsetForeground().Render(" [•] None [ ] API Key\n")) - } else { - content.WriteString(itemStyle.UnsetForeground().Render(" [ ] None [•] API Key\n")) + content.WriteString(itemStyle.UnsetForeground().Render(" [•] None [ ] Project API Key [ ] Agent API Key\n")) + } else if m.authCursor == 1 { + content.WriteString(itemStyle.UnsetForeground().Render(" [ ] None [•] Project API Key [ ] Agent API Key\n")) + } else if m.authCursor == 2 { + content.WriteString(itemStyle.UnsetForeground().Render(" [ ] None [ ] Project API Key [•] Agent API Key\n")) } }