Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
545 changes: 545 additions & 0 deletions cmd/credentials.go

Large diffs are not rendered by default.

159 changes: 159 additions & 0 deletions cmd/proxies/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package proxies

import (
"context"
"fmt"

"github.com/kernel/cli/pkg/table"
"github.com/kernel/cli/pkg/util"
"github.com/kernel/kernel-go-sdk"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
)

func (p ProxyCmd) Check(ctx context.Context, in ProxyCheckInput) error {
if in.Output != "" && in.Output != "json" {
return fmt.Errorf("unsupported --output value: use 'json'")
}

if in.Output != "json" {
pterm.Info.Printf("Running health check on proxy %s...\n", in.ID)
}

proxy, err := p.proxies.Check(ctx, in.ID)
if err != nil {
return util.CleanedUpSdkError{Err: err}
}

if in.Output == "json" {
return util.PrintPrettyJSON(proxy)
}

// Display proxy details after check
rows := pterm.TableData{{"Property", "Value"}}

rows = append(rows, []string{"ID", proxy.ID})

name := proxy.Name
if name == "" {
name = "-"
}
rows = append(rows, []string{"Name", name})
rows = append(rows, []string{"Type", string(proxy.Type)})

// Display protocol (default to https if not set)
protocol := string(proxy.Protocol)
if protocol == "" {
protocol = "https"
}
rows = append(rows, []string{"Protocol", protocol})

// Display IP address if available
if proxy.IPAddress != "" {
rows = append(rows, []string{"IP Address", proxy.IPAddress})
}

// Display type-specific config details
rows = append(rows, getProxyCheckConfigRows(proxy)...)

// Display status with color
status := string(proxy.Status)
if status == "" {
status = "-"
} else if proxy.Status == kernel.ProxyCheckResponseStatusAvailable {
status = pterm.Green(status)
} else if proxy.Status == kernel.ProxyCheckResponseStatusUnavailable {
status = pterm.Red(status)
}
rows = append(rows, []string{"Status", status})

// Display last checked timestamp
lastChecked := util.FormatLocal(proxy.LastChecked)
rows = append(rows, []string{"Last Checked", lastChecked})

table.PrintTableNoPad(rows, true)

// Print a summary message
if proxy.Status == kernel.ProxyCheckResponseStatusAvailable {
pterm.Success.Println("Proxy health check passed")
} else {
pterm.Warning.Println("Proxy health check failed - proxy is unavailable")
}

return nil
}

func getProxyCheckConfigRows(proxy *kernel.ProxyCheckResponse) [][]string {
var rows [][]string
config := &proxy.Config

switch proxy.Type {
case kernel.ProxyCheckResponseTypeDatacenter, kernel.ProxyCheckResponseTypeIsp:
if config.Country != "" {
rows = append(rows, []string{"Country", config.Country})
}
case kernel.ProxyCheckResponseTypeResidential:
if config.Country != "" {
rows = append(rows, []string{"Country", config.Country})
}
if config.City != "" {
rows = append(rows, []string{"City", config.City})
}
if config.State != "" {
rows = append(rows, []string{"State", config.State})
}
if config.Zip != "" {
rows = append(rows, []string{"ZIP", config.Zip})
}
if config.Asn != "" {
rows = append(rows, []string{"ASN", config.Asn})
}
if config.Os != "" {
rows = append(rows, []string{"OS", config.Os})
}
case kernel.ProxyCheckResponseTypeMobile:
if config.Country != "" {
rows = append(rows, []string{"Country", config.Country})
}
if config.City != "" {
rows = append(rows, []string{"City", config.City})
}
if config.State != "" {
rows = append(rows, []string{"State", config.State})
}
if config.Zip != "" {
rows = append(rows, []string{"ZIP", config.Zip})
}
if config.Asn != "" {
rows = append(rows, []string{"ASN", config.Asn})
}
if config.Carrier != "" {
rows = append(rows, []string{"Carrier", config.Carrier})
}
case kernel.ProxyCheckResponseTypeCustom:
if config.Host != "" {
rows = append(rows, []string{"Host", config.Host})
}
if config.Port != 0 {
rows = append(rows, []string{"Port", fmt.Sprintf("%d", config.Port)})
}
if config.Username != "" {
rows = append(rows, []string{"Username", config.Username})
}
hasPassword := "No"
if config.HasPassword {
hasPassword = "Yes"
}
rows = append(rows, []string{"Has Password", hasPassword})
}

return rows
}

func runProxiesCheck(cmd *cobra.Command, args []string) error {
client := util.GetKernelClient(cmd)
output, _ := cmd.Flags().GetString("output")
svc := client.Proxies
p := ProxyCmd{proxies: &svc}
return p.Check(cmd.Context(), ProxyCheckInput{ID: args[0], Output: output})
}
8 changes: 8 additions & 0 deletions cmd/proxies/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type FakeProxyService struct {
GetFunc func(ctx context.Context, id string, opts ...option.RequestOption) (*kernel.ProxyGetResponse, error)
NewFunc func(ctx context.Context, body kernel.ProxyNewParams, opts ...option.RequestOption) (*kernel.ProxyNewResponse, error)
DeleteFunc func(ctx context.Context, id string, opts ...option.RequestOption) error
CheckFunc func(ctx context.Context, id string, opts ...option.RequestOption) (*kernel.ProxyCheckResponse, error)
}

func (f *FakeProxyService) List(ctx context.Context, opts ...option.RequestOption) (*[]kernel.ProxyListResponse, error) {
Expand Down Expand Up @@ -72,6 +73,13 @@ func (f *FakeProxyService) Delete(ctx context.Context, id string, opts ...option
return nil
}

func (f *FakeProxyService) Check(ctx context.Context, id string, opts ...option.RequestOption) (*kernel.ProxyCheckResponse, error) {
if f.CheckFunc != nil {
return f.CheckFunc(ctx, id, opts...)
}
return &kernel.ProxyCheckResponse{ID: id, Type: kernel.ProxyCheckResponseTypeDatacenter}, nil
}

// Helper function to create test proxy responses
func createDatacenterProxy(id, name, country string) kernel.ProxyListResponse {
return kernel.ProxyListResponse{
Expand Down
12 changes: 12 additions & 0 deletions cmd/proxies/proxies.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,21 @@ var proxiesDeleteCmd = &cobra.Command{
RunE: runProxiesDelete,
}

var proxiesCheckCmd = &cobra.Command{
Use: "check <id>",
Short: "Run a health check on a proxy",
Long: "Run a health check on a proxy to verify it's working and update its status.",
Args: cobra.ExactArgs(1),
RunE: runProxiesCheck,
}

func init() {
// Add subcommands
ProxiesCmd.AddCommand(proxiesListCmd)
ProxiesCmd.AddCommand(proxiesGetCmd)
ProxiesCmd.AddCommand(proxiesCreateCmd)
ProxiesCmd.AddCommand(proxiesDeleteCmd)
ProxiesCmd.AddCommand(proxiesCheckCmd)

// Add output flags
proxiesListCmd.Flags().StringP("output", "o", "", "Output format: json for raw API response")
Expand Down Expand Up @@ -99,4 +108,7 @@ func init() {

// Delete flags
proxiesDeleteCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt")

// Check flags
proxiesCheckCmd.Flags().StringP("output", "o", "", "Output format: json for raw API response")
}
6 changes: 6 additions & 0 deletions cmd/proxies/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type ProxyService interface {
Get(ctx context.Context, id string, opts ...option.RequestOption) (res *kernel.ProxyGetResponse, err error)
New(ctx context.Context, body kernel.ProxyNewParams, opts ...option.RequestOption) (res *kernel.ProxyNewResponse, err error)
Delete(ctx context.Context, id string, opts ...option.RequestOption) (err error)
Check(ctx context.Context, id string, opts ...option.RequestOption) (res *kernel.ProxyCheckResponse, err error)
}

// ProxyCmd handles proxy operations independent of cobra.
Expand Down Expand Up @@ -56,3 +57,8 @@ type ProxyDeleteInput struct {
ID string
SkipConfirm bool
}

type ProxyCheckInput struct {
ID string
Output string
}
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ func init() {
rootCmd.AddCommand(profilesCmd)
rootCmd.AddCommand(proxies.ProxiesCmd)
rootCmd.AddCommand(extensionsCmd)
rootCmd.AddCommand(credentialsCmd)
rootCmd.AddCommand(createCmd)
rootCmd.AddCommand(mcp.MCPCmd)
rootCmd.AddCommand(upgradeCmd)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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/kernel/kernel-go-sdk v0.26.1-0.20260117115631-ebae1efd3449
github.com/kernel/kernel-go-sdk v0.26.0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SDK version downgraded instead of upgraded as intended

High Severity

The PR description states the SDK should be updated to commit 8dc9875576aea5daa08fa44faeee0bd0e99888a8, but the go.mod change actually downgrades from v0.26.1-0.20260117115631-ebae1efd3449 (a pseudo-version after v0.26.0) to v0.26.0 (the tagged release). This is a version downgrade, not an upgrade. The new credentials and proxy check commands may fail if they depend on SDK methods not present in v0.26.0.

Additional Locations (1)

Fix in Cursor Fix in Web

github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/pterm/pterm v0.12.80
github.com/samber/lo v1.51.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +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.26.1-0.20260117115631-ebae1efd3449 h1:NDrHon1ahRBI1xlatalhEUxjRk03EX5MtZ7Q1sapsLs=
github.com/kernel/kernel-go-sdk v0.26.1-0.20260117115631-ebae1efd3449/go.mod h1:EeZzSuHZVeHKxKCPUzxou2bovNGhXaz0RXrSqKNf1AQ=
github.com/kernel/kernel-go-sdk v0.26.0 h1:IBiEohSSZN5MEZjmnfqseT3tEip6+xg7Zxr79vJYMBA=
github.com/kernel/kernel-go-sdk v0.26.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=
Expand Down