diff --git a/cmd/agent_auth.go b/cmd/agent_auth.go new file mode 100644 index 0000000..5df27f4 --- /dev/null +++ b/cmd/agent_auth.go @@ -0,0 +1,778 @@ +package cmd + +import ( + "context" + "fmt" + "time" + + "github.com/onkernel/cli/pkg/util" + kernel "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk/packages/pagination" + "github.com/pkg/browser" + "github.com/pterm/pterm" + "github.com/spf13/cobra" +) + +// AgentAuthService defines the subset of the Kernel SDK agent auth client that we use. +type AgentAuthService interface { + New(ctx context.Context, body kernel.AgentAuthNewParams, opts ...option.RequestOption) (*kernel.AuthAgent, error) + Get(ctx context.Context, id string, opts ...option.RequestOption) (*kernel.AuthAgent, error) + List(ctx context.Context, query kernel.AgentAuthListParams, opts ...option.RequestOption) (*pagination.OffsetPagination[kernel.AuthAgent], error) + Delete(ctx context.Context, id string, opts ...option.RequestOption) error +} + +// AgentAuthInvocationService defines the subset we use for agent auth invocations. +type AgentAuthInvocationService interface { + New(ctx context.Context, body kernel.AgentAuthInvocationNewParams, opts ...option.RequestOption) (*kernel.AuthAgentInvocationCreateResponse, error) + Get(ctx context.Context, invocationID string, opts ...option.RequestOption) (*kernel.AgentAuthInvocationResponse, error) + Submit(ctx context.Context, invocationID string, body kernel.AgentAuthInvocationSubmitParams, opts ...option.RequestOption) (*kernel.AgentAuthSubmitResponse, error) +} + +// AgentAuthCmd handles agent auth operations. +type AgentAuthCmd struct { + auth AgentAuthService + invocations AgentAuthInvocationService + browsers BrowsersService +} + +// CreateInput holds input for creating an auth agent. +type CreateInput struct { + Domain string + ProfileName string + CredentialName string + LoginURL string + AllowedDomains []string +} + +// Create creates a new auth agent. +func (a AgentAuthCmd) Create(ctx context.Context, in CreateInput) error { + params := kernel.AgentAuthNewParams{ + AuthAgentCreateRequest: kernel.AuthAgentCreateRequestParam{ + Domain: in.Domain, + ProfileName: in.ProfileName, + }, + } + + if in.CredentialName != "" { + params.AuthAgentCreateRequest.CredentialName = kernel.Opt(in.CredentialName) + } + if in.LoginURL != "" { + params.AuthAgentCreateRequest.LoginURL = kernel.Opt(in.LoginURL) + } + if len(in.AllowedDomains) > 0 { + params.AuthAgentCreateRequest.AllowedDomains = in.AllowedDomains + } + + agent, err := a.auth.New(ctx, params) + if err != nil { + return util.CleanedUpSdkError{Err: err} + } + + rows := pterm.TableData{{"Property", "Value"}} + rows = append(rows, []string{"ID", agent.ID}) + rows = append(rows, []string{"Domain", agent.Domain}) + rows = append(rows, []string{"Profile Name", agent.ProfileName}) + rows = append(rows, []string{"Status", string(agent.Status)}) + if agent.CredentialName != "" { + rows = append(rows, []string{"Credential", agent.CredentialName}) + } + if len(agent.AllowedDomains) > 0 { + rows = append(rows, []string{"Allowed Domains", fmt.Sprintf("%v", agent.AllowedDomains)}) + } + + PrintTableNoPad(rows, true) + + pterm.Println() + pterm.Info.Println("Next step: Start the authentication flow with:") + pterm.Printf(" kernel agents auth invoke %s\n", agent.ID) + + return nil +} + +// InvokeInput holds input for starting an invocation. +type InvokeInput struct { + AuthAgentID string + SaveCredentialAs string + NoBrowser bool +} + +// Invoke starts an auth invocation and handles the hosted UI flow. +func (a AgentAuthCmd) Invoke(ctx context.Context, in InvokeInput) error { + params := kernel.AgentAuthInvocationNewParams{ + AuthAgentInvocationCreateRequest: kernel.AuthAgentInvocationCreateRequestParam{ + AuthAgentID: in.AuthAgentID, + }, + } + + if in.SaveCredentialAs != "" { + params.AuthAgentInvocationCreateRequest.SaveCredentialAs = kernel.Opt(in.SaveCredentialAs) + } + + invocation, err := a.invocations.New(ctx, params) + if err != nil { + return util.CleanedUpSdkError{Err: err} + } + + pterm.Info.Println("Invocation created") + pterm.Println(fmt.Sprintf(" Invocation ID: %s", invocation.InvocationID)) + pterm.Println(fmt.Sprintf(" Type: %s", invocation.Type)) + pterm.Println(fmt.Sprintf(" Expires: %s", invocation.ExpiresAt.Format(time.RFC3339))) + pterm.Println() + + pterm.Info.Println("Open this URL in your browser to log in:") + pterm.Println() + pterm.Println(fmt.Sprintf(" %s", invocation.HostedURL)) + pterm.Println() + + if !in.NoBrowser { + if err := browser.OpenURL(invocation.HostedURL); err != nil { + pterm.Warning.Printf("Could not open browser automatically: %v\n", err) + } else { + pterm.Info.Println("(Opened in browser)") + } + } + + pterm.Println() + pterm.Info.Println("Polling for completion...") + + startTime := time.Now() + maxWaitTime := 5 * time.Minute + pollInterval := 2 * time.Second + + for time.Since(startTime) < maxWaitTime { + state, err := a.invocations.Get(ctx, invocation.InvocationID) + if err != nil { + return util.CleanedUpSdkError{Err: err} + } + + elapsed := int(time.Since(startTime).Seconds()) + pterm.Println(fmt.Sprintf(" [%ds] status=%s, step=%s", elapsed, state.Status, state.Step)) + + // Show live view URL on first poll + if state.LiveViewURL != "" && elapsed < 5 { + pterm.Println(fmt.Sprintf(" Live view: %s", state.LiveViewURL)) + } + + // Show pending fields if any + if len(state.PendingFields) > 0 { + var fieldNames []string + for _, f := range state.PendingFields { + fieldNames = append(fieldNames, f.Name) + } + pterm.Println(fmt.Sprintf(" Fields: %v", fieldNames)) + } + + // Show SSO buttons if any + if len(state.PendingSSOButtons) > 0 { + var providers []string + for _, b := range state.PendingSSOButtons { + providers = append(providers, b.Provider) + } + pterm.Println(fmt.Sprintf(" SSO buttons: %v", providers)) + } + + // Show external action message + if state.Step == kernel.AgentAuthInvocationResponseStepAwaitingExternalAction && state.ExternalActionMessage != "" { + pterm.Warning.Printf(" External action required: %s\n", state.ExternalActionMessage) + } + + switch state.Status { + case kernel.AgentAuthInvocationResponseStatusSuccess: + pterm.Println() + pterm.Success.Println("Login completed successfully!") + + // Fetch and display the auth agent + agent, err := a.auth.Get(ctx, in.AuthAgentID) + if err != nil { + pterm.Warning.Printf("Could not fetch auth agent: %v\n", err) + return nil + } + + pterm.Println() + pterm.Println(fmt.Sprintf(" Auth Agent: %s", agent.ID)) + pterm.Println(fmt.Sprintf(" Profile: %s", agent.ProfileName)) + pterm.Println(fmt.Sprintf(" Domain: %s", agent.Domain)) + pterm.Println(fmt.Sprintf(" Status: %s", agent.Status)) + if agent.CredentialName != "" { + pterm.Println(fmt.Sprintf(" Credential: %s", agent.CredentialName)) + } + + pterm.Println() + pterm.Info.Printf("You can now create browsers with profile: %s\n", agent.ProfileName) + return nil + + case kernel.AgentAuthInvocationResponseStatusExpired: + pterm.Println() + pterm.Error.Println("Invocation expired") + return nil + + case kernel.AgentAuthInvocationResponseStatusCanceled: + pterm.Println() + pterm.Error.Println("Invocation was canceled") + return nil + + case kernel.AgentAuthInvocationResponseStatusFailed: + pterm.Println() + pterm.Error.Println("Invocation failed") + if state.ErrorMessage != "" { + pterm.Error.Printf(" Error: %s\n", state.ErrorMessage) + } + return nil + } + + time.Sleep(pollInterval) + } + + pterm.Error.Println("Polling timed out after 5 minutes") + return nil +} + +// GetInput holds input for getting an auth agent. +type GetInput struct { + ID string +} + +// Get retrieves an auth agent by ID. +func (a AgentAuthCmd) Get(ctx context.Context, in GetInput) error { + agent, err := a.auth.Get(ctx, in.ID) + if err != nil { + return util.CleanedUpSdkError{Err: err} + } + + rows := pterm.TableData{{"Property", "Value"}} + rows = append(rows, []string{"ID", agent.ID}) + rows = append(rows, []string{"Domain", agent.Domain}) + rows = append(rows, []string{"Profile Name", agent.ProfileName}) + rows = append(rows, []string{"Status", string(agent.Status)}) + if agent.CredentialName != "" { + rows = append(rows, []string{"Credential", agent.CredentialName}) + } + if len(agent.AllowedDomains) > 0 { + rows = append(rows, []string{"Allowed Domains", fmt.Sprintf("%v", agent.AllowedDomains)}) + } + rows = append(rows, []string{"Can Reauth", fmt.Sprintf("%t", agent.CanReauth)}) + rows = append(rows, []string{"Has Selectors", fmt.Sprintf("%t", agent.HasSelectors)}) + if !agent.LastAuthCheckAt.IsZero() { + rows = append(rows, []string{"Last Auth Check", agent.LastAuthCheckAt.Format(time.RFC3339)}) + } + + PrintTableNoPad(rows, true) + return nil +} + +// DeleteInput holds input for deleting an auth agent. +type DeleteInput struct { + ID string +} + +// Delete removes an auth agent. +func (a AgentAuthCmd) Delete(ctx context.Context, in DeleteInput) error { + if err := a.auth.Delete(ctx, in.ID); err != nil { + return util.CleanedUpSdkError{Err: err} + } + + pterm.Success.Printf("Auth agent %s deleted\n", in.ID) + return nil +} + +// ListInput holds input for listing auth agents. +type ListInput struct { + Domain string + ProfileName string + Limit int64 + Offset int64 +} + +// List lists auth agents. +func (a AgentAuthCmd) List(ctx context.Context, in ListInput) error { + params := kernel.AgentAuthListParams{} + if in.Domain != "" { + params.Domain = kernel.Opt(in.Domain) + } + if in.ProfileName != "" { + params.ProfileName = kernel.Opt(in.ProfileName) + } + if in.Limit > 0 { + params.Limit = kernel.Opt(in.Limit) + } + if in.Offset > 0 { + params.Offset = kernel.Opt(in.Offset) + } + + page, err := a.auth.List(ctx, params) + if err != nil { + return util.CleanedUpSdkError{Err: err} + } + + agents := page.Items + if len(agents) == 0 { + pterm.Info.Println("No auth agents found") + return nil + } + + rows := pterm.TableData{{"ID", "Domain", "Profile", "Status", "Can Reauth"}} + for _, agent := range agents { + rows = append(rows, []string{ + agent.ID, + agent.Domain, + agent.ProfileName, + string(agent.Status), + fmt.Sprintf("%t", agent.CanReauth), + }) + } + + PrintTableNoPad(rows, true) + return nil +} + +// GetInvocationInput holds input for getting an invocation. +type GetInvocationInput struct { + InvocationID string +} + +// GetInvocation retrieves an invocation by ID. +func (a AgentAuthCmd) GetInvocation(ctx context.Context, in GetInvocationInput) error { + state, err := a.invocations.Get(ctx, in.InvocationID) + if err != nil { + return util.CleanedUpSdkError{Err: err} + } + + rows := pterm.TableData{{"Property", "Value"}} + rows = append(rows, []string{"Status", string(state.Status)}) + rows = append(rows, []string{"Step", string(state.Step)}) + rows = append(rows, []string{"Type", string(state.Type)}) + rows = append(rows, []string{"Domain", state.Domain}) + rows = append(rows, []string{"App Name", state.AppName}) + rows = append(rows, []string{"Expires", state.ExpiresAt.Format(time.RFC3339)}) + if state.LiveViewURL != "" { + rows = append(rows, []string{"Live View", state.LiveViewURL}) + } + if state.ErrorMessage != "" { + rows = append(rows, []string{"Error", state.ErrorMessage}) + } + if state.ExternalActionMessage != "" { + rows = append(rows, []string{"External Action", state.ExternalActionMessage}) + } + + PrintTableNoPad(rows, true) + + // Show pending fields if any + if len(state.PendingFields) > 0 { + pterm.Println() + pterm.Info.Println("Pending Fields:") + fieldRows := pterm.TableData{{"Name", "Type", "Label", "Required"}} + for _, f := range state.PendingFields { + fieldRows = append(fieldRows, []string{f.Name, string(f.Type), f.Label, fmt.Sprintf("%t", f.Required)}) + } + PrintTableNoPad(fieldRows, true) + } + + // Show SSO buttons if any + if len(state.PendingSSOButtons) > 0 { + pterm.Println() + pterm.Info.Println("SSO Buttons:") + buttonRows := pterm.TableData{{"Provider", "Label", "Selector"}} + for _, b := range state.PendingSSOButtons { + buttonRows = append(buttonRows, []string{b.Provider, b.Label, b.Selector}) + } + PrintTableNoPad(buttonRows, true) + } + + return nil +} + +// SubmitInvocationInput holds input for submitting to an invocation. +type SubmitInvocationInput struct { + InvocationID string + FieldValues map[string]string + SSOButton string +} + +// SubmitInvocation submits field values or clicks an SSO button. +func (a AgentAuthCmd) SubmitInvocation(ctx context.Context, in SubmitInvocationInput) error { + var params kernel.AgentAuthInvocationSubmitParams + + if in.SSOButton != "" { + params.OfSSOButton = &kernel.AgentAuthInvocationSubmitParamsBodySSOButton{ + SSOButton: in.SSOButton, + } + } else if len(in.FieldValues) > 0 { + params.OfFieldValues = &kernel.AgentAuthInvocationSubmitParamsBodyFieldValues{ + FieldValues: in.FieldValues, + } + } else { + return fmt.Errorf("must provide either --field or --sso-button") + } + + resp, err := a.invocations.Submit(ctx, in.InvocationID, params) + if err != nil { + return util.CleanedUpSdkError{Err: err} + } + + if resp.Accepted { + pterm.Success.Println("Submission accepted") + } else { + pterm.Warning.Println("Submission not accepted") + } + return nil +} + +// --- Cobra wiring --- + +var agentsCmd = &cobra.Command{ + Use: "agents", + Short: "Manage agents", + Long: "Commands for managing Kernel agents", +} + +var agentsAuthCmd = &cobra.Command{ + Use: "auth", + Short: "Manage auth agents", + Long: "Commands for managing agent authentication", +} + +var agentsAuthCreateCmd = &cobra.Command{ + Use: "create", + Short: "Create an auth agent", + Long: "Create a new auth agent for a domain and profile", + Args: cobra.NoArgs, + RunE: runAgentsAuthCreate, +} + +var agentsAuthInvokeCmd = &cobra.Command{ + Use: "invoke [auth-agent-id]", + Short: "Start an auth invocation", + Long: "Start an authentication invocation using the hosted UI flow", + Args: cobra.MaximumNArgs(1), + RunE: runAgentsAuthInvoke, +} + +var agentsAuthGetCmd = &cobra.Command{ + Use: "get ", + Short: "Get auth agent details", + Args: cobra.ExactArgs(1), + RunE: runAgentsAuthGet, +} + +var agentsAuthDeleteCmd = &cobra.Command{ + Use: "delete ", + Short: "Delete an auth agent", + Args: cobra.ExactArgs(1), + RunE: runAgentsAuthDelete, +} + +var agentsAuthListCmd = &cobra.Command{ + Use: "list", + Short: "List auth agents", + Long: "List auth agents with optional filters", + Args: cobra.NoArgs, + RunE: runAgentsAuthList, +} + +var agentsAuthInvocationCmd = &cobra.Command{ + Use: "invocation", + Short: "Manage auth invocations", + Long: "Commands for managing individual auth invocations", +} + +var agentsAuthInvocationGetCmd = &cobra.Command{ + Use: "get ", + Short: "Get invocation details", + Long: "Get the current status and details of an invocation", + Args: cobra.ExactArgs(1), + RunE: runAgentsAuthInvocationGet, +} + +var agentsAuthInvocationSubmitCmd = &cobra.Command{ + Use: "submit ", + Short: "Submit to an invocation", + Long: "Submit field values or click an SSO button for an invocation", + Args: cobra.ExactArgs(1), + RunE: runAgentsAuthInvocationSubmit, +} + +func init() { + agentsAuthCmd.AddCommand(agentsAuthCreateCmd) + agentsAuthCmd.AddCommand(agentsAuthInvokeCmd) + agentsAuthCmd.AddCommand(agentsAuthGetCmd) + agentsAuthCmd.AddCommand(agentsAuthDeleteCmd) + agentsAuthCmd.AddCommand(agentsAuthListCmd) + agentsAuthCmd.AddCommand(agentsAuthInvocationCmd) + agentsCmd.AddCommand(agentsAuthCmd) + + // invocation subcommands + agentsAuthInvocationCmd.AddCommand(agentsAuthInvocationGetCmd) + agentsAuthInvocationCmd.AddCommand(agentsAuthInvocationSubmitCmd) + + // create flags + agentsAuthCreateCmd.Flags().String("domain", "", "Target domain to authenticate with") + agentsAuthCreateCmd.Flags().String("profile-name", "", "Profile name to use or create") + agentsAuthCreateCmd.Flags().String("credential-name", "", "Optional credential name to link") + agentsAuthCreateCmd.Flags().String("login-url", "", "Optional login URL to skip discovery") + agentsAuthCreateCmd.Flags().StringSlice("allowed-domains", nil, "Additional allowed domains for OAuth redirects") + agentsAuthCreateCmd.Flags().BoolP("interactive", "i", false, "Interactive mode - select profile and credential from lists") + + // invoke flags + agentsAuthInvokeCmd.Flags().String("save-credential-as", "", "Save credentials under this name after successful login") + agentsAuthInvokeCmd.Flags().Bool("no-browser", false, "Don't automatically open browser") + agentsAuthInvokeCmd.Flags().BoolP("interactive", "i", false, "Interactive mode - select auth agent from list") + + // list flags + agentsAuthListCmd.Flags().String("domain", "", "Filter by domain") + agentsAuthListCmd.Flags().String("profile-name", "", "Filter by profile name") + agentsAuthListCmd.Flags().Int64("limit", 0, "Maximum number of results") + agentsAuthListCmd.Flags().Int64("offset", 0, "Number of results to skip") + + // invocation submit flags + agentsAuthInvocationSubmitCmd.Flags().StringToString("field", nil, "Field values to submit (key=value)") + agentsAuthInvocationSubmitCmd.Flags().String("sso-button", "", "SSO button selector to click") +} + +func runAgentsAuthCreate(cmd *cobra.Command, args []string) error { + client := getKernelClient(cmd) + ctx := cmd.Context() + + domain, _ := cmd.Flags().GetString("domain") + profileName, _ := cmd.Flags().GetString("profile-name") + credentialName, _ := cmd.Flags().GetString("credential-name") + loginURL, _ := cmd.Flags().GetString("login-url") + allowedDomains, _ := cmd.Flags().GetStringSlice("allowed-domains") + interactive, _ := cmd.Flags().GetBool("interactive") + + if interactive { + var err error + + // Prompt for domain if not provided + if domain == "" { + domain, err = pterm.DefaultInteractiveTextInput. + WithDefaultText("Enter target domain (e.g., github.com)"). + Show() + if err != nil { + return fmt.Errorf("failed to get domain: %w", err) + } + } + + // Let user select profile or create new + if profileName == "" { + profiles, err := client.Profiles.List(ctx) + if err != nil { + return util.CleanedUpSdkError{Err: err} + } + + options := []string{"[Create new profile]"} + profileMap := make(map[string]string) // display -> name + if profiles != nil { + for _, p := range *profiles { + display := p.Name + if display == "" { + display = p.ID + } + options = append(options, display) + profileMap[display] = p.Name + if p.Name == "" { + profileMap[display] = p.ID + } + } + } + + selected, err := pterm.DefaultInteractiveSelect. + WithDefaultText("Select a profile"). + WithOptions(options). + WithFilter(true). + Show() + if err != nil { + return fmt.Errorf("failed to select profile: %w", err) + } + + if selected == "[Create new profile]" { + profileName, err = pterm.DefaultInteractiveTextInput. + WithDefaultText("Enter new profile name"). + Show() + if err != nil { + return fmt.Errorf("failed to get profile name: %w", err) + } + } else { + profileName = profileMap[selected] + } + } + + // Let user select credential (optional) + if credentialName == "" { + credsPage, err := client.Credentials.List(ctx, kernel.CredentialListParams{}) + if err != nil { + pterm.Warning.Printf("Could not fetch credentials: %v\n", err) + } else if len(credsPage.Items) > 0 { + options := []string{"[Skip - no credential]"} + credMap := make(map[string]string) // display -> name + for _, c := range credsPage.Items { + display := fmt.Sprintf("%s (%s)", c.Name, c.Domain) + options = append(options, display) + credMap[display] = c.Name + } + + selected, err := pterm.DefaultInteractiveSelect. + WithDefaultText("Select a credential (optional)"). + WithOptions(options). + WithFilter(true). + Show() + if err != nil { + return fmt.Errorf("failed to select credential: %w", err) + } + + if selected != "[Skip - no credential]" { + credentialName = credMap[selected] + } + } + } + + // Validate we have required fields + if domain == "" { + return fmt.Errorf("domain is required") + } + if profileName == "" { + return fmt.Errorf("profile name is required") + } + } else { + // Non-interactive mode - validate required fields + if domain == "" { + return fmt.Errorf("--domain is required (or use -i for interactive mode)") + } + if profileName == "" { + return fmt.Errorf("--profile-name is required (or use -i for interactive mode)") + } + } + + svc := client.Agents.Auth + a := AgentAuthCmd{auth: &svc} + + return a.Create(ctx, CreateInput{ + Domain: domain, + ProfileName: profileName, + CredentialName: credentialName, + LoginURL: loginURL, + AllowedDomains: allowedDomains, + }) +} + +func runAgentsAuthInvoke(cmd *cobra.Command, args []string) error { + client := getKernelClient(cmd) + ctx := cmd.Context() + + saveCredentialAs, _ := cmd.Flags().GetString("save-credential-as") + noBrowser, _ := cmd.Flags().GetBool("no-browser") + interactive, _ := cmd.Flags().GetBool("interactive") + + var authAgentID string + if len(args) > 0 { + authAgentID = args[0] + } + + if interactive || authAgentID == "" { + // Fetch auth agents and let user select + page, err := client.Agents.Auth.List(ctx, kernel.AgentAuthListParams{}) + if err != nil { + return util.CleanedUpSdkError{Err: err} + } + + if len(page.Items) == 0 { + pterm.Error.Println("No auth agents found. Create one first with: kernel agents auth create") + return nil + } + + options := []string{} + agentMap := make(map[string]string) // display -> id + for _, agent := range page.Items { + display := fmt.Sprintf("%s - %s (%s)", agent.ID[:8], agent.Domain, agent.ProfileName) + options = append(options, display) + agentMap[display] = agent.ID + } + + selected, err := pterm.DefaultInteractiveSelect. + WithDefaultText("Select an auth agent to invoke"). + WithOptions(options). + WithFilter(true). + Show() + if err != nil { + return fmt.Errorf("failed to select auth agent: %w", err) + } + + authAgentID = agentMap[selected] + } + + if authAgentID == "" { + return fmt.Errorf("auth-agent-id is required (or use -i for interactive mode)") + } + + svc := client.Agents.Auth + invocationsSvc := client.Agents.Auth.Invocations + a := AgentAuthCmd{auth: &svc, invocations: &invocationsSvc} + + return a.Invoke(ctx, InvokeInput{ + AuthAgentID: authAgentID, + SaveCredentialAs: saveCredentialAs, + NoBrowser: noBrowser, + }) +} + +func runAgentsAuthGet(cmd *cobra.Command, args []string) error { + client := getKernelClient(cmd) + + svc := client.Agents.Auth + a := AgentAuthCmd{auth: &svc} + + return a.Get(cmd.Context(), GetInput{ID: args[0]}) +} + +func runAgentsAuthDelete(cmd *cobra.Command, args []string) error { + client := getKernelClient(cmd) + + svc := client.Agents.Auth + a := AgentAuthCmd{auth: &svc} + + return a.Delete(cmd.Context(), DeleteInput{ID: args[0]}) +} + +func runAgentsAuthList(cmd *cobra.Command, args []string) error { + client := getKernelClient(cmd) + + domain, _ := cmd.Flags().GetString("domain") + profileName, _ := cmd.Flags().GetString("profile-name") + limit, _ := cmd.Flags().GetInt64("limit") + offset, _ := cmd.Flags().GetInt64("offset") + + svc := client.Agents.Auth + a := AgentAuthCmd{auth: &svc} + + return a.List(cmd.Context(), ListInput{ + Domain: domain, + ProfileName: profileName, + Limit: limit, + Offset: offset, + }) +} + +func runAgentsAuthInvocationGet(cmd *cobra.Command, args []string) error { + client := getKernelClient(cmd) + + invocationsSvc := client.Agents.Auth.Invocations + a := AgentAuthCmd{invocations: &invocationsSvc} + + return a.GetInvocation(cmd.Context(), GetInvocationInput{InvocationID: args[0]}) +} + +func runAgentsAuthInvocationSubmit(cmd *cobra.Command, args []string) error { + client := getKernelClient(cmd) + + fieldValues, _ := cmd.Flags().GetStringToString("field") + ssoButton, _ := cmd.Flags().GetString("sso-button") + + invocationsSvc := client.Agents.Auth.Invocations + a := AgentAuthCmd{invocations: &invocationsSvc} + + return a.SubmitInvocation(cmd.Context(), SubmitInvocationInput{ + InvocationID: args[0], + FieldValues: fieldValues, + SSOButton: ssoButton, + }) +} diff --git a/cmd/app.go b/cmd/app.go index 16cfbbf..90fa140 100644 --- a/cmd/app.go +++ b/cmd/app.go @@ -5,7 +5,7 @@ import ( "strings" "github.com/onkernel/cli/pkg/util" - "github.com/onkernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk" "github.com/pterm/pterm" "github.com/samber/lo" "github.com/spf13/cobra" diff --git a/cmd/browser_pools.go b/cmd/browser_pools.go index 08a31fe..d688805 100644 --- a/cmd/browser_pools.go +++ b/cmd/browser_pools.go @@ -7,8 +7,8 @@ import ( "strings" "github.com/onkernel/cli/pkg/util" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/pterm/pterm" "github.com/spf13/cobra" ) diff --git a/cmd/browsers.go b/cmd/browsers.go index eb4e27b..f2852b5 100644 --- a/cmd/browsers.go +++ b/cmd/browsers.go @@ -16,18 +16,18 @@ import ( "strings" "github.com/onkernel/cli/pkg/util" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" - "github.com/onkernel/kernel-go-sdk/packages/pagination" - "github.com/onkernel/kernel-go-sdk/packages/ssestream" - "github.com/onkernel/kernel-go-sdk/shared" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk/packages/pagination" + "github.com/kernel/kernel-go-sdk/packages/ssestream" + "github.com/kernel/kernel-go-sdk/shared" "github.com/pterm/pterm" "github.com/spf13/cobra" "github.com/spf13/pflag" ) // BrowsersService defines the subset of the Kernel SDK browser client that we use. -// See https://github.com/onkernel/kernel-go-sdk/blob/main/browser.go +// See https://github.com/kernel/kernel-go-sdk/blob/main/browser.go type BrowsersService interface { Get(ctx context.Context, id string, opts ...option.RequestOption) (res *kernel.BrowserGetResponse, err error) List(ctx context.Context, query kernel.BrowserListParams, opts ...option.RequestOption) (res *pagination.OffsetPagination[kernel.BrowserListResponse], err error) diff --git a/cmd/browsers_test.go b/cmd/browsers_test.go index 2fe489d..901480d 100644 --- a/cmd/browsers_test.go +++ b/cmd/browsers_test.go @@ -13,11 +13,11 @@ import ( "testing" "time" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" - "github.com/onkernel/kernel-go-sdk/packages/pagination" - "github.com/onkernel/kernel-go-sdk/packages/ssestream" - "github.com/onkernel/kernel-go-sdk/shared" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk/packages/pagination" + "github.com/kernel/kernel-go-sdk/packages/ssestream" + "github.com/kernel/kernel-go-sdk/shared" "github.com/pterm/pterm" "github.com/stretchr/testify/assert" ) diff --git a/cmd/credentials.go b/cmd/credentials.go new file mode 100644 index 0000000..a89c41a --- /dev/null +++ b/cmd/credentials.go @@ -0,0 +1,409 @@ +package cmd + +import ( + "context" + "fmt" + "time" + + "github.com/onkernel/cli/pkg/util" + kernel "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk/packages/pagination" + "github.com/pterm/pterm" + "github.com/spf13/cobra" +) + +// CredentialService defines the subset of the Kernel SDK credential client that we use. +type CredentialService interface { + New(ctx context.Context, body kernel.CredentialNewParams, opts ...option.RequestOption) (*kernel.Credential, error) + Get(ctx context.Context, idOrName string, opts ...option.RequestOption) (*kernel.Credential, error) + Update(ctx context.Context, idOrName string, body kernel.CredentialUpdateParams, opts ...option.RequestOption) (*kernel.Credential, error) + List(ctx context.Context, query kernel.CredentialListParams, opts ...option.RequestOption) (*pagination.OffsetPagination[kernel.Credential], error) + Delete(ctx context.Context, idOrName string, opts ...option.RequestOption) error + TotpCode(ctx context.Context, idOrName string, opts ...option.RequestOption) (*kernel.CredentialTotpCodeResponse, error) +} + +// CredentialsCmd handles credential operations. +type CredentialsCmd struct { + credentials CredentialService +} + +// CreateCredentialInput holds input for creating a credential. +type CreateCredentialInput struct { + Name string + Domain string + Values map[string]string + SSOProvider string + TotpSecret string +} + +// Create creates a new credential. +func (c CredentialsCmd) Create(ctx context.Context, in CreateCredentialInput) error { + params := kernel.CredentialNewParams{ + CreateCredentialRequest: kernel.CreateCredentialRequestParam{ + Name: in.Name, + Domain: in.Domain, + Values: in.Values, + }, + } + + if in.SSOProvider != "" { + params.CreateCredentialRequest.SSOProvider = kernel.Opt(in.SSOProvider) + } + if in.TotpSecret != "" { + params.CreateCredentialRequest.TotpSecret = kernel.Opt(in.TotpSecret) + } + + cred, err := c.credentials.New(ctx, params) + if err != nil { + return util.CleanedUpSdkError{Err: err} + } + + rows := pterm.TableData{{"Property", "Value"}} + rows = append(rows, []string{"ID", cred.ID}) + rows = append(rows, []string{"Name", cred.Name}) + rows = append(rows, []string{"Domain", cred.Domain}) + rows = append(rows, []string{"Created", cred.CreatedAt.Format(time.RFC3339)}) + if cred.SSOProvider != "" { + rows = append(rows, []string{"SSO Provider", cred.SSOProvider}) + } + rows = append(rows, []string{"Has TOTP", fmt.Sprintf("%t", cred.HasTotpSecret)}) + if cred.TotpCode != "" { + rows = append(rows, []string{"TOTP Code", cred.TotpCode}) + rows = append(rows, []string{"TOTP Expires", cred.TotpCodeExpiresAt.Format(time.RFC3339)}) + } + + PrintTableNoPad(rows, true) + return nil +} + +// GetCredentialInput holds input for getting a credential. +type GetCredentialInput struct { + IDOrName string +} + +// Get retrieves a credential by ID or name. +func (c CredentialsCmd) Get(ctx context.Context, in GetCredentialInput) error { + cred, err := c.credentials.Get(ctx, in.IDOrName) + if err != nil { + return util.CleanedUpSdkError{Err: err} + } + + rows := pterm.TableData{{"Property", "Value"}} + rows = append(rows, []string{"ID", cred.ID}) + rows = append(rows, []string{"Name", cred.Name}) + rows = append(rows, []string{"Domain", cred.Domain}) + rows = append(rows, []string{"Created", cred.CreatedAt.Format(time.RFC3339)}) + rows = append(rows, []string{"Updated", cred.UpdatedAt.Format(time.RFC3339)}) + if cred.SSOProvider != "" { + rows = append(rows, []string{"SSO Provider", cred.SSOProvider}) + } + rows = append(rows, []string{"Has TOTP", fmt.Sprintf("%t", cred.HasTotpSecret)}) + + PrintTableNoPad(rows, true) + return nil +} + +// ListCredentialsInput holds input for listing credentials. +type ListCredentialsInput struct { + Domain string + Limit int64 + Offset int64 +} + +// List lists credentials. +func (c CredentialsCmd) List(ctx context.Context, in ListCredentialsInput) error { + params := kernel.CredentialListParams{} + if in.Domain != "" { + params.Domain = kernel.Opt(in.Domain) + } + if in.Limit > 0 { + params.Limit = kernel.Opt(in.Limit) + } + if in.Offset > 0 { + params.Offset = kernel.Opt(in.Offset) + } + + page, err := c.credentials.List(ctx, params) + if err != nil { + return util.CleanedUpSdkError{Err: err} + } + + creds := page.Items + if len(creds) == 0 { + pterm.Info.Println("No credentials found") + return nil + } + + rows := pterm.TableData{{"ID", "Name", "Domain", "SSO", "TOTP"}} + for _, cred := range creds { + sso := "-" + if cred.SSOProvider != "" { + sso = cred.SSOProvider + } + rows = append(rows, []string{ + cred.ID, + cred.Name, + cred.Domain, + sso, + fmt.Sprintf("%t", cred.HasTotpSecret), + }) + } + + PrintTableNoPad(rows, true) + return nil +} + +// UpdateCredentialInput holds input for updating a credential. +type UpdateCredentialInput struct { + IDOrName string + Name string + Values map[string]string + SSOProvider string + TotpSecret string +} + +// Update updates a credential. +func (c CredentialsCmd) Update(ctx context.Context, in UpdateCredentialInput) error { + params := kernel.CredentialUpdateParams{ + UpdateCredentialRequest: kernel.UpdateCredentialRequestParam{}, + } + + if in.Name != "" { + params.UpdateCredentialRequest.Name = kernel.Opt(in.Name) + } + if len(in.Values) > 0 { + params.UpdateCredentialRequest.Values = in.Values + } + if in.SSOProvider != "" { + params.UpdateCredentialRequest.SSOProvider = kernel.Opt(in.SSOProvider) + } + if in.TotpSecret != "" { + params.UpdateCredentialRequest.TotpSecret = kernel.Opt(in.TotpSecret) + } + + cred, err := c.credentials.Update(ctx, in.IDOrName, params) + if err != nil { + return util.CleanedUpSdkError{Err: err} + } + + rows := pterm.TableData{{"Property", "Value"}} + rows = append(rows, []string{"ID", cred.ID}) + rows = append(rows, []string{"Name", cred.Name}) + rows = append(rows, []string{"Domain", cred.Domain}) + rows = append(rows, []string{"Updated", cred.UpdatedAt.Format(time.RFC3339)}) + if cred.SSOProvider != "" { + rows = append(rows, []string{"SSO Provider", cred.SSOProvider}) + } + rows = append(rows, []string{"Has TOTP", fmt.Sprintf("%t", cred.HasTotpSecret)}) + if cred.TotpCode != "" { + rows = append(rows, []string{"TOTP Code", cred.TotpCode}) + rows = append(rows, []string{"TOTP Expires", cred.TotpCodeExpiresAt.Format(time.RFC3339)}) + } + + pterm.Success.Println("Credential updated") + PrintTableNoPad(rows, true) + return nil +} + +// DeleteCredentialInput holds input for deleting a credential. +type DeleteCredentialInput struct { + IDOrName string +} + +// Delete removes a credential. +func (c CredentialsCmd) Delete(ctx context.Context, in DeleteCredentialInput) error { + if err := c.credentials.Delete(ctx, in.IDOrName); err != nil { + return util.CleanedUpSdkError{Err: err} + } + + pterm.Success.Printf("Credential %s deleted\n", in.IDOrName) + return nil +} + +// TotpCodeInput holds input for getting a TOTP code. +type TotpCodeInput struct { + IDOrName string +} + +// TotpCode gets the current TOTP code for a credential. +func (c CredentialsCmd) TotpCode(ctx context.Context, in TotpCodeInput) error { + resp, err := c.credentials.TotpCode(ctx, in.IDOrName) + if err != nil { + return util.CleanedUpSdkError{Err: err} + } + + pterm.Info.Printf("TOTP Code: %s\n", resp.Code) + pterm.Info.Printf("Expires: %s\n", resp.ExpiresAt.Format(time.RFC3339)) + return nil +} + +// --- Cobra wiring --- + +var credentialsCmd = &cobra.Command{ + Use: "credentials", + Short: "Manage credentials", + Long: "Commands for managing stored credentials for automatic authentication", +} + +var credentialsCreateCmd = &cobra.Command{ + Use: "create", + Short: "Create a credential", + Long: "Create a new credential for storing login information", + Args: cobra.NoArgs, + RunE: runCredentialsCreate, +} + +var credentialsGetCmd = &cobra.Command{ + Use: "get ", + Short: "Get credential details", + Long: "Retrieve a credential by its ID or name", + Args: cobra.ExactArgs(1), + RunE: runCredentialsGet, +} + +var credentialsListCmd = &cobra.Command{ + Use: "list", + Short: "List credentials", + Long: "List credentials with optional filters", + Args: cobra.NoArgs, + RunE: runCredentialsList, +} + +var credentialsUpdateCmd = &cobra.Command{ + Use: "update ", + Short: "Update a credential", + Long: "Update an existing credential's name or values", + Args: cobra.ExactArgs(1), + RunE: runCredentialsUpdate, +} + +var credentialsDeleteCmd = &cobra.Command{ + Use: "delete ", + Short: "Delete a credential", + Long: "Delete a credential by its ID or name", + Args: cobra.ExactArgs(1), + RunE: runCredentialsDelete, +} + +var credentialsTotpCodeCmd = &cobra.Command{ + Use: "totp-code ", + Short: "Get TOTP code", + Long: "Get the current 6-digit TOTP code for a credential with a configured totp_secret", + Args: cobra.ExactArgs(1), + RunE: runCredentialsTotpCode, +} + +func init() { + credentialsCmd.AddCommand(credentialsCreateCmd) + credentialsCmd.AddCommand(credentialsGetCmd) + credentialsCmd.AddCommand(credentialsListCmd) + credentialsCmd.AddCommand(credentialsUpdateCmd) + credentialsCmd.AddCommand(credentialsDeleteCmd) + credentialsCmd.AddCommand(credentialsTotpCodeCmd) + + // create flags + credentialsCreateCmd.Flags().String("name", "", "Unique name for the credential (required)") + credentialsCreateCmd.Flags().String("domain", "", "Target domain this credential is for (required)") + credentialsCreateCmd.Flags().StringToString("value", nil, "Field values (key=value, can specify multiple)") + credentialsCreateCmd.Flags().String("sso-provider", "", "SSO provider (e.g., google, github, microsoft)") + credentialsCreateCmd.Flags().String("totp-secret", "", "Base32-encoded TOTP secret for 2FA") + _ = credentialsCreateCmd.MarkFlagRequired("name") + _ = credentialsCreateCmd.MarkFlagRequired("domain") + + // list flags + credentialsListCmd.Flags().String("domain", "", "Filter by domain") + credentialsListCmd.Flags().Int64("limit", 0, "Maximum number of results") + credentialsListCmd.Flags().Int64("offset", 0, "Number of results to skip") + + // update flags + credentialsUpdateCmd.Flags().String("name", "", "New name for the credential") + credentialsUpdateCmd.Flags().StringToString("value", nil, "Field values to update (key=value)") + credentialsUpdateCmd.Flags().String("sso-provider", "", "SSO provider (e.g., google, github, microsoft)") + credentialsUpdateCmd.Flags().String("totp-secret", "", "Base32-encoded TOTP secret for 2FA") +} + +func runCredentialsCreate(cmd *cobra.Command, args []string) error { + client := getKernelClient(cmd) + + name, _ := cmd.Flags().GetString("name") + domain, _ := cmd.Flags().GetString("domain") + values, _ := cmd.Flags().GetStringToString("value") + ssoProvider, _ := cmd.Flags().GetString("sso-provider") + totpSecret, _ := cmd.Flags().GetString("totp-secret") + + svc := client.Credentials + c := CredentialsCmd{credentials: &svc} + + return c.Create(cmd.Context(), CreateCredentialInput{ + Name: name, + Domain: domain, + Values: values, + SSOProvider: ssoProvider, + TotpSecret: totpSecret, + }) +} + +func runCredentialsGet(cmd *cobra.Command, args []string) error { + client := getKernelClient(cmd) + + svc := client.Credentials + c := CredentialsCmd{credentials: &svc} + + return c.Get(cmd.Context(), GetCredentialInput{IDOrName: args[0]}) +} + +func runCredentialsList(cmd *cobra.Command, args []string) error { + client := getKernelClient(cmd) + + domain, _ := cmd.Flags().GetString("domain") + limit, _ := cmd.Flags().GetInt64("limit") + offset, _ := cmd.Flags().GetInt64("offset") + + svc := client.Credentials + c := CredentialsCmd{credentials: &svc} + + return c.List(cmd.Context(), ListCredentialsInput{ + Domain: domain, + Limit: limit, + Offset: offset, + }) +} + +func runCredentialsUpdate(cmd *cobra.Command, args []string) error { + client := getKernelClient(cmd) + + name, _ := cmd.Flags().GetString("name") + values, _ := cmd.Flags().GetStringToString("value") + ssoProvider, _ := cmd.Flags().GetString("sso-provider") + totpSecret, _ := cmd.Flags().GetString("totp-secret") + + svc := client.Credentials + c := CredentialsCmd{credentials: &svc} + + return c.Update(cmd.Context(), UpdateCredentialInput{ + IDOrName: args[0], + Name: name, + Values: values, + SSOProvider: ssoProvider, + TotpSecret: totpSecret, + }) +} + +func runCredentialsDelete(cmd *cobra.Command, args []string) error { + client := getKernelClient(cmd) + + svc := client.Credentials + c := CredentialsCmd{credentials: &svc} + + return c.Delete(cmd.Context(), DeleteCredentialInput{IDOrName: args[0]}) +} + +func runCredentialsTotpCode(cmd *cobra.Command, args []string) error { + client := getKernelClient(cmd) + + svc := client.Credentials + c := CredentialsCmd{credentials: &svc} + + return c.TotpCode(cmd.Context(), TotpCodeInput{IDOrName: args[0]}) +} diff --git a/cmd/deploy.go b/cmd/deploy.go index 600dca6..3d2e1bf 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -16,8 +16,8 @@ import ( "github.com/joho/godotenv" "github.com/onkernel/cli/pkg/util" - kernel "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + kernel "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/pterm/pterm" "github.com/samber/lo" "github.com/spf13/cobra" diff --git a/cmd/extensions.go b/cmd/extensions.go index 68b880b..6a7ca07 100644 --- a/cmd/extensions.go +++ b/cmd/extensions.go @@ -11,8 +11,8 @@ import ( "time" "github.com/onkernel/cli/pkg/util" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/pterm/pterm" "github.com/spf13/cobra" ) diff --git a/cmd/extensions_test.go b/cmd/extensions_test.go index 9a7897d..4a1770c 100644 --- a/cmd/extensions_test.go +++ b/cmd/extensions_test.go @@ -12,8 +12,8 @@ import ( "testing" "time" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/pterm/pterm" "github.com/stretchr/testify/assert" ) diff --git a/cmd/invoke.go b/cmd/invoke.go index 913a643..00fe502 100644 --- a/cmd/invoke.go +++ b/cmd/invoke.go @@ -14,8 +14,8 @@ import ( "time" "github.com/onkernel/cli/pkg/util" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/pterm/pterm" "github.com/spf13/cobra" ) diff --git a/cmd/logs.go b/cmd/logs.go index 7d7dbf4..3e1d5ff 100644 --- a/cmd/logs.go +++ b/cmd/logs.go @@ -5,8 +5,8 @@ import ( "time" "github.com/onkernel/cli/pkg/util" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/pterm/pterm" "github.com/spf13/cobra" ) diff --git a/cmd/profiles.go b/cmd/profiles.go index c068453..7b4ee47 100644 --- a/cmd/profiles.go +++ b/cmd/profiles.go @@ -10,8 +10,8 @@ import ( "os" "github.com/onkernel/cli/pkg/util" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/pterm/pterm" "github.com/spf13/cobra" ) diff --git a/cmd/profiles_test.go b/cmd/profiles_test.go index 3573197..3833924 100644 --- a/cmd/profiles_test.go +++ b/cmd/profiles_test.go @@ -11,8 +11,8 @@ import ( "testing" "time" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/pterm/pterm" "github.com/stretchr/testify/assert" ) diff --git a/cmd/proxies/common_test.go b/cmd/proxies/common_test.go index 0e4d607..9f815a6 100644 --- a/cmd/proxies/common_test.go +++ b/cmd/proxies/common_test.go @@ -6,8 +6,8 @@ import ( "os" "testing" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/pterm/pterm" ) diff --git a/cmd/proxies/create.go b/cmd/proxies/create.go index 0a4eb46..f349d38 100644 --- a/cmd/proxies/create.go +++ b/cmd/proxies/create.go @@ -6,7 +6,7 @@ import ( "github.com/onkernel/cli/pkg/table" "github.com/onkernel/cli/pkg/util" - "github.com/onkernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk" "github.com/pterm/pterm" "github.com/spf13/cobra" ) diff --git a/cmd/proxies/create_test.go b/cmd/proxies/create_test.go index 6669c55..cda254f 100644 --- a/cmd/proxies/create_test.go +++ b/cmd/proxies/create_test.go @@ -5,8 +5,8 @@ import ( "errors" "testing" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/stretchr/testify/assert" ) diff --git a/cmd/proxies/delete_test.go b/cmd/proxies/delete_test.go index 192e893..8edce63 100644 --- a/cmd/proxies/delete_test.go +++ b/cmd/proxies/delete_test.go @@ -6,8 +6,8 @@ import ( "net/http" "testing" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/stretchr/testify/assert" ) diff --git a/cmd/proxies/get.go b/cmd/proxies/get.go index 49c89ee..f995af6 100644 --- a/cmd/proxies/get.go +++ b/cmd/proxies/get.go @@ -6,7 +6,7 @@ import ( "github.com/onkernel/cli/pkg/table" "github.com/onkernel/cli/pkg/util" - "github.com/onkernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk" "github.com/pterm/pterm" "github.com/spf13/cobra" ) diff --git a/cmd/proxies/get_test.go b/cmd/proxies/get_test.go index 9f726c9..922abe3 100644 --- a/cmd/proxies/get_test.go +++ b/cmd/proxies/get_test.go @@ -6,8 +6,8 @@ import ( "net/http" "testing" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/stretchr/testify/assert" ) diff --git a/cmd/proxies/list.go b/cmd/proxies/list.go index e5a86f5..6bc638b 100644 --- a/cmd/proxies/list.go +++ b/cmd/proxies/list.go @@ -7,7 +7,7 @@ import ( "github.com/onkernel/cli/pkg/table" "github.com/onkernel/cli/pkg/util" - "github.com/onkernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk" "github.com/pterm/pterm" "github.com/spf13/cobra" ) diff --git a/cmd/proxies/list_test.go b/cmd/proxies/list_test.go index aada9d8..dcb6f71 100644 --- a/cmd/proxies/list_test.go +++ b/cmd/proxies/list_test.go @@ -5,8 +5,8 @@ import ( "errors" "testing" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/stretchr/testify/assert" ) diff --git a/cmd/proxies/types.go b/cmd/proxies/types.go index ae871b6..979f071 100644 --- a/cmd/proxies/types.go +++ b/cmd/proxies/types.go @@ -3,8 +3,8 @@ package proxies import ( "context" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" ) // ProxyService defines the subset of the Kernel SDK proxy client that we use. diff --git a/cmd/root.go b/cmd/root.go index 1b4a6b3..8261c6b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -16,8 +16,8 @@ import ( "github.com/onkernel/cli/pkg/auth" "github.com/onkernel/cli/pkg/update" "github.com/onkernel/cli/pkg/util" - "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/pterm/pterm" "github.com/spf13/cobra" ) @@ -139,6 +139,8 @@ func init() { rootCmd.AddCommand(profilesCmd) rootCmd.AddCommand(proxies.ProxiesCmd) rootCmd.AddCommand(extensionsCmd) + rootCmd.AddCommand(agentsCmd) + rootCmd.AddCommand(credentialsCmd) rootCmd.AddCommand(createCmd) rootCmd.AddCommand(mcp.MCPCmd) diff --git a/go.mod b/go.mod index cdfe589..e7f22d2 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1 github.com/golang-jwt/jwt/v5 v5.2.2 github.com/joho/godotenv v1.5.1 - github.com/onkernel/kernel-go-sdk v0.24.0 + github.com/kernel/kernel-go-sdk v0.25.0 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/pterm/pterm v0.12.80 github.com/samber/lo v1.51.0 diff --git a/go.sum b/go.sum index 3172668..847e196 100644 --- a/go.sum +++ b/go.sum @@ -64,6 +64,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kernel/kernel-go-sdk v0.25.0 h1:I6EpQKcOasiuVi6gX8HcqEIxK9dAs6xMGSnlUW7AxXY= +github.com/kernel/kernel-go-sdk v0.25.0/go.mod h1:EeZzSuHZVeHKxKCPUzxou2bovNGhXaz0RXrSqKNf1AQ= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= @@ -91,8 +93,6 @@ github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0= github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8= github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig= -github.com/onkernel/kernel-go-sdk v0.24.0 h1:M5GdjmdASnpet9UezVEadPpzWyjBfoyqnLwHf/tFoVE= -github.com/onkernel/kernel-go-sdk v0.24.0/go.mod h1:t80buN1uCA/hwvm4D2SpjTJzZWcV7bWOFo9d7qdXD8M= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/pkg/auth/client.go b/pkg/auth/client.go index 58bf2c4..6446967 100644 --- a/pkg/auth/client.go +++ b/pkg/auth/client.go @@ -5,8 +5,8 @@ import ( "fmt" "os" - kernel "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + kernel "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/pterm/pterm" ) diff --git a/pkg/util/client.go b/pkg/util/client.go index 6b12f1d..db67b89 100644 --- a/pkg/util/client.go +++ b/pkg/util/client.go @@ -9,8 +9,8 @@ import ( "os" "sync/atomic" - kernel "github.com/onkernel/kernel-go-sdk" - "github.com/onkernel/kernel-go-sdk/option" + kernel "github.com/kernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk/option" "github.com/pterm/pterm" "github.com/spf13/cobra" ) diff --git a/pkg/util/errors.go b/pkg/util/errors.go index 45a4843..76fe233 100644 --- a/pkg/util/errors.go +++ b/pkg/util/errors.go @@ -6,7 +6,7 @@ import ( "fmt" "io" - "github.com/onkernel/kernel-go-sdk" + "github.com/kernel/kernel-go-sdk" ) // CleanedUpSdkError extracts a message field from the raw JSON resposne.