diff --git a/cmd/cloud.go b/cmd/cloud.go index b9690c0c..0cf2034d 100644 --- a/cmd/cloud.go +++ b/cmd/cloud.go @@ -112,7 +112,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.AgentsKey) }) tui.ShowSuccess("Project imported successfully") } diff --git a/cmd/project.go b/cmd/project.go index 2f6c3627..97aa0d5d 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -36,22 +36,33 @@ Use the subcommands to manage your projects.`, }, } -func saveEnv(dir string, apikey string) { +func saveEnv(dir string, apikey string, agentsKey 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 + var found map[string]bool = map[string]bool{ + "AGENTUITY_API_KEY": false, + "AGENTUITY_AGENTS_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_AGENTS_KEY" { + envLines[i].Val = apikey + found["AGENTUITY_AGENTS_KEY"] = true } } - if !found { + if !found["AGENTUITY_API_KEY"] { envLines = append(envLines, env.EnvLine{Key: "AGENTUITY_API_KEY", Val: apikey}) } + if !found["AGENTUITY_AGENTS_KEY"] { + envLines = append(envLines, env.EnvLine{Key: "AGENTUITY_AGENTS_KEY", Val: apikey}) + } if err := env.WriteEnvFile(filename, envLines); err != nil { errsystem.New(errsystem.ErrWriteConfigurationFile, err, errsystem.WithContextMessage("Failed to write .env file")).ShowErrorAndExit() } @@ -65,6 +76,7 @@ type InitProjectArgs struct { Name string Description string EnableWebhookAuth bool + AuthType string Provider *templates.TemplateRules Agents []project.AgentConfig } @@ -78,6 +90,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 +139,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.AgentsKey) return result } @@ -540,7 +553,8 @@ Examples: Description: description, Provider: rules, Agents: agents, - EnableWebhookAuth: authType != "none", + EnableWebhookAuth: authType == "project" || authType == "agent", + AuthType: authType, }) // remember our choices @@ -562,7 +576,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 == "agent" { 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 +825,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, agent, or none)") projectNewCmd.Flags().String("action", "github-app", "The action to take for the project (github-action, github-app, none)") } diff --git a/internal/project/project.go b/internal/project/project.go index 6fd46d72..0c9019b2 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -42,6 +42,7 @@ type initProjectResult struct { type ProjectData struct { APIKey string `json:"api_key"` + AgentsKey string `json:"agentsKey"` ProjectId string `json:"id"` OrgId string `json:"orgId"` Env map[string]string `json:"env"` @@ -60,6 +61,7 @@ type InitProjectArgs struct { Description string EnableWebhookAuth bool Agents []AgentConfig + AuthType string } // InitProject will create a new project in the organization. @@ -80,6 +82,7 @@ func InitProject(ctx context.Context, logger logger.Logger, args InitProjectArgs "description": args.Description, "enableWebhookAuth": args.EnableWebhookAuth, "agents": agents, + "authType": args.AuthType, } logger.Trace("sending new project payload: %s", cstr.JSONStringify(payload)) @@ -89,7 +92,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 } @@ -375,6 +377,7 @@ type ProjectImportResponse struct { ID string `json:"id"` Agents []AgentConfig `json:"agents"` APIKey string `json:"apiKey"` + AgentsKey string `json:"agentsKey"` IOAuthToken string `json:"ioAuthToken"` } diff --git a/internal/ui/project.go b/internal/ui/project.go index e3507a90..7567b68e 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 == "agent" { + 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")) } }