From 251eb3c7e74d28233d5181989c9b446bda588db7 Mon Sep 17 00:00:00 2001 From: Tim Pietrusky Date: Wed, 25 Mar 2026 16:18:24 +0100 Subject: [PATCH 1/3] feat: add hub command to browse and deploy from the runpod hub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit adds `runpodctl hub` command group to browse, search, and get details from the runpod hub marketplace. also adds `--hub-id` flag to `serverless create` to deploy endpoints directly from hub listings without needing to create a template first. new commands: - hub list (with --type, --category, --order-by, --owner filters) - hub search - hub get serverless create --hub-id resolves the listing, extracts the build image and config, creates an inline template via graphql, and deploys the endpoint — matching the web ui flow. --- cmd/hub/get.go | 53 ++++ cmd/hub/hub.go | 18 ++ cmd/hub/hub_test.go | 25 ++ cmd/hub/list.go | 73 ++++++ cmd/hub/search.go | 78 ++++++ cmd/root.go | 3 + cmd/serverless/create.go | 129 +++++++++- docs/runpodctl.md | 4 +- docs/runpodctl_billing.md | 2 +- docs/runpodctl_billing_network-volume.md | 2 +- docs/runpodctl_billing_pods.md | 2 +- docs/runpodctl_billing_serverless.md | 2 +- docs/runpodctl_completion.md | 2 +- docs/runpodctl_datacenter.md | 2 +- docs/runpodctl_datacenter_list.md | 2 +- docs/runpodctl_doctor.md | 2 +- docs/runpodctl_gpu.md | 2 +- docs/runpodctl_gpu_list.md | 2 +- docs/runpodctl_hub.md | 28 +++ docs/runpodctl_hub_get.md | 33 +++ docs/runpodctl_hub_list.md | 46 ++++ docs/runpodctl_hub_search.md | 41 +++ docs/runpodctl_model.md | 2 +- docs/runpodctl_model_add.md | 2 +- docs/runpodctl_model_list.md | 2 +- docs/runpodctl_model_remove.md | 2 +- docs/runpodctl_network-volume.md | 2 +- docs/runpodctl_network-volume_create.md | 2 +- docs/runpodctl_network-volume_delete.md | 2 +- docs/runpodctl_network-volume_get.md | 2 +- docs/runpodctl_network-volume_list.md | 2 +- docs/runpodctl_network-volume_update.md | 2 +- docs/runpodctl_pod.md | 2 +- docs/runpodctl_pod_create.md | 2 +- docs/runpodctl_pod_delete.md | 2 +- docs/runpodctl_pod_get.md | 2 +- docs/runpodctl_pod_list.md | 2 +- docs/runpodctl_pod_reset.md | 2 +- docs/runpodctl_pod_restart.md | 2 +- docs/runpodctl_pod_start.md | 2 +- docs/runpodctl_pod_stop.md | 2 +- docs/runpodctl_pod_update.md | 2 +- docs/runpodctl_receive.md | 2 +- docs/runpodctl_registry.md | 2 +- docs/runpodctl_registry_create.md | 2 +- docs/runpodctl_registry_delete.md | 2 +- docs/runpodctl_registry_get.md | 2 +- docs/runpodctl_registry_list.md | 2 +- docs/runpodctl_send.md | 2 +- docs/runpodctl_serverless.md | 2 +- docs/runpodctl_serverless_create.md | 17 +- docs/runpodctl_serverless_delete.md | 2 +- docs/runpodctl_serverless_get.md | 2 +- docs/runpodctl_serverless_list.md | 2 +- docs/runpodctl_serverless_update.md | 2 +- docs/runpodctl_ssh.md | 2 +- docs/runpodctl_ssh_add-key.md | 2 +- docs/runpodctl_ssh_info.md | 2 +- docs/runpodctl_ssh_list-keys.md | 2 +- docs/runpodctl_template.md | 2 +- docs/runpodctl_template_create.md | 2 +- docs/runpodctl_template_delete.md | 2 +- docs/runpodctl_template_get.md | 2 +- docs/runpodctl_template_list.md | 2 +- docs/runpodctl_template_search.md | 2 +- docs/runpodctl_template_update.md | 2 +- docs/runpodctl_update.md | 2 +- docs/runpodctl_user.md | 2 +- docs/runpodctl_version.md | 2 +- e2e/cli_test.go | 293 ++++++++++++++++++++++ internal/api/endpoints.go | 80 +++++- internal/api/hub.go | 301 +++++++++++++++++++++++ 72 files changed, 1263 insertions(+), 71 deletions(-) create mode 100644 cmd/hub/get.go create mode 100644 cmd/hub/hub.go create mode 100644 cmd/hub/hub_test.go create mode 100644 cmd/hub/list.go create mode 100644 cmd/hub/search.go create mode 100644 docs/runpodctl_hub.md create mode 100644 docs/runpodctl_hub_get.md create mode 100644 docs/runpodctl_hub_list.md create mode 100644 docs/runpodctl_hub_search.md create mode 100644 internal/api/hub.go diff --git a/cmd/hub/get.go b/cmd/hub/get.go new file mode 100644 index 0000000..7a6781a --- /dev/null +++ b/cmd/hub/get.go @@ -0,0 +1,53 @@ +package hub + +import ( + "fmt" + "strings" + + "github.com/runpod/runpodctl/internal/api" + "github.com/runpod/runpodctl/internal/output" + + "github.com/spf13/cobra" +) + +var getCmd = &cobra.Command{ + Use: "get ", + Short: "get hub repo details", + Long: `get details for a hub repo by id or owner/name. + +examples: + runpodctl hub get clma1kziv00064iog9u6acj6z # by listing id + runpodctl hub get runpod/worker-vllm # by owner/name`, + Args: cobra.ExactArgs(1), + RunE: runGet, +} + +func runGet(cmd *cobra.Command, args []string) error { + arg := args[0] + + client, err := api.NewClient() + if err != nil { + output.Error(err) + return err + } + + var listing *api.Listing + + if strings.Contains(arg, "/") { + parts := strings.SplitN(arg, "/", 2) + if parts[0] == "" || parts[1] == "" { + return fmt.Errorf("invalid format %q; use owner/name or a listing id", arg) + } + listing, err = client.GetListingFromRepo(parts[0], parts[1]) + } else { + listing, err = client.GetListing(arg) + } + + if err != nil { + output.Error(err) + return fmt.Errorf("failed to get hub repo: %w", err) + } + + format := output.ParseFormat(cmd.Flag("output").Value.String()) + return output.Print(listing, &output.Config{Format: format}) +} diff --git a/cmd/hub/hub.go b/cmd/hub/hub.go new file mode 100644 index 0000000..6e07bbb --- /dev/null +++ b/cmd/hub/hub.go @@ -0,0 +1,18 @@ +package hub + +import ( + "github.com/spf13/cobra" +) + +// Cmd is the hub command group +var Cmd = &cobra.Command{ + Use: "hub", + Short: "browse the runpod hub", + Long: "browse and search the runpod hub for deployable repos", +} + +func init() { + Cmd.AddCommand(listCmd) + Cmd.AddCommand(searchCmd) + Cmd.AddCommand(getCmd) +} diff --git a/cmd/hub/hub_test.go b/cmd/hub/hub_test.go new file mode 100644 index 0000000..607e4db --- /dev/null +++ b/cmd/hub/hub_test.go @@ -0,0 +1,25 @@ +package hub + +import ( + "testing" +) + +func TestHubCmd_Structure(t *testing.T) { + if Cmd.Use != "hub" { + t.Errorf("expected use 'hub', got %s", Cmd.Use) + } + + expectedSubcommands := []string{"list", "search ", "get "} + for _, expected := range expectedSubcommands { + found := false + for _, cmd := range Cmd.Commands() { + if cmd.Use == expected { + found = true + break + } + } + if !found { + t.Errorf("expected subcommand %q not found", expected) + } + } +} diff --git a/cmd/hub/list.go b/cmd/hub/list.go new file mode 100644 index 0000000..0be7d51 --- /dev/null +++ b/cmd/hub/list.go @@ -0,0 +1,73 @@ +package hub + +import ( + "github.com/runpod/runpodctl/internal/api" + "github.com/runpod/runpodctl/internal/output" + + "github.com/spf13/cobra" +) + +var listCmd = &cobra.Command{ + Use: "list", + Short: "list hub repos", + Long: `list repos from the runpod hub. + +by default shows the top 10 repos ordered by stars. + +examples: + runpodctl hub list # top 10 by stars + runpodctl hub list --type SERVERLESS # only serverless repos + runpodctl hub list --type POD # only pod repos + runpodctl hub list --category ai --limit 20 # filter by category + runpodctl hub list --order-by deploys # order by deploys + runpodctl hub list --owner runpod # filter by repo owner`, + Args: cobra.NoArgs, + RunE: runList, +} + +var ( + listCategory string + listOrderBy string + listOrderDir string + listLimit int + listOffset int + listOwner string + listType string +) + +func init() { + listCmd.Flags().StringVar(&listCategory, "category", "", "filter by category") + listCmd.Flags().StringVar(&listOrderBy, "order-by", "stars", "order by: createdAt, deploys, releasedAt, stars, updatedAt, views") + listCmd.Flags().StringVar(&listOrderDir, "order-dir", "desc", "order direction: asc or desc") + listCmd.Flags().IntVar(&listLimit, "limit", 10, "max number of results to return") + listCmd.Flags().IntVar(&listOffset, "offset", 0, "offset for pagination") + listCmd.Flags().StringVar(&listOwner, "owner", "", "filter by repo owner") + listCmd.Flags().StringVar(&listType, "type", "", "filter by type: POD or SERVERLESS") +} + +func runList(cmd *cobra.Command, args []string) error { + client, err := api.NewClient() + if err != nil { + output.Error(err) + return err + } + + opts := &api.ListingsOptions{ + Category: listCategory, + OrderBy: listOrderBy, + OrderDirection: listOrderDir, + Limit: listLimit, + Offset: listOffset, + Owner: listOwner, + Type: listType, + } + + listings, err := client.ListListings(opts) + if err != nil { + output.Error(err) + return err + } + + format := output.ParseFormat(cmd.Flag("output").Value.String()) + return output.Print(listings, &output.Config{Format: format}) +} diff --git a/cmd/hub/search.go b/cmd/hub/search.go new file mode 100644 index 0000000..e955418 --- /dev/null +++ b/cmd/hub/search.go @@ -0,0 +1,78 @@ +package hub + +import ( + "fmt" + + "github.com/runpod/runpodctl/internal/api" + "github.com/runpod/runpodctl/internal/output" + + "github.com/spf13/cobra" +) + +var searchCmd = &cobra.Command{ + Use: "search ", + Short: "search hub repos", + Long: `search for repos in the runpod hub. + +examples: + runpodctl hub search vllm # search for "vllm" + runpodctl hub search whisper --type SERVERLESS # search serverless repos + runpodctl hub search stable-diffusion --limit 5 # limit results`, + Args: cobra.ExactArgs(1), + RunE: runSearch, +} + +var ( + searchCategory string + searchOrderBy string + searchOrderDir string + searchLimit int + searchOffset int + searchOwner string + searchType string +) + +func init() { + searchCmd.Flags().StringVar(&searchCategory, "category", "", "filter by category") + searchCmd.Flags().StringVar(&searchOrderBy, "order-by", "stars", "order by: createdAt, deploys, releasedAt, stars, updatedAt, views") + searchCmd.Flags().StringVar(&searchOrderDir, "order-dir", "desc", "order direction: asc or desc") + searchCmd.Flags().IntVar(&searchLimit, "limit", 10, "max number of results to return") + searchCmd.Flags().IntVar(&searchOffset, "offset", 0, "offset for pagination") + searchCmd.Flags().StringVar(&searchOwner, "owner", "", "filter by repo owner") + searchCmd.Flags().StringVar(&searchType, "type", "", "filter by type: POD or SERVERLESS") +} + +func runSearch(cmd *cobra.Command, args []string) error { + searchTerm := args[0] + + client, err := api.NewClient() + if err != nil { + output.Error(err) + return err + } + + opts := &api.ListingsOptions{ + SearchQuery: searchTerm, + Category: searchCategory, + OrderBy: searchOrderBy, + OrderDirection: searchOrderDir, + Limit: searchLimit, + Offset: searchOffset, + Owner: searchOwner, + Type: searchType, + } + + listings, err := client.ListListings(opts) + if err != nil { + output.Error(err) + return err + } + + if len(listings) == 0 { + fmt.Printf("no hub repos found matching %q\n", searchTerm) + return nil + } + + format := output.ParseFormat(cmd.Flag("output").Value.String()) + return output.Print(listings, &output.Config{Format: format}) +} diff --git a/cmd/root.go b/cmd/root.go index adbd9f0..205b8ef 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -9,6 +9,7 @@ import ( "github.com/runpod/runpodctl/cmd/datacenter" "github.com/runpod/runpodctl/cmd/doctor" "github.com/runpod/runpodctl/cmd/gpu" + "github.com/runpod/runpodctl/cmd/hub" "github.com/runpod/runpodctl/cmd/legacy" "github.com/runpod/runpodctl/cmd/model" "github.com/runpod/runpodctl/cmd/pod" @@ -43,6 +44,7 @@ resources: pod manage gpu pods serverless manage serverless endpoints (alias: sls) template manage templates (alias: tpl) + hub browse the runpod hub model manage model repository network-volume manage network volumes (alias: nv) registry manage container registry auth (alias: reg) @@ -87,6 +89,7 @@ func registerCommands() { rootCmd.AddCommand(model.Cmd) rootCmd.AddCommand(volume.Cmd) rootCmd.AddCommand(registry.Cmd) + rootCmd.AddCommand(hub.Cmd) // Info commands rootCmd.AddCommand(user.Cmd) diff --git a/cmd/serverless/create.go b/cmd/serverless/create.go index f94e017..d06e57c 100644 --- a/cmd/serverless/create.go +++ b/cmd/serverless/create.go @@ -1,7 +1,9 @@ package serverless import ( + "encoding/json" "fmt" + "math/rand" "strings" "github.com/runpod/runpodctl/internal/api" @@ -13,26 +15,38 @@ import ( var createCmd = &cobra.Command{ Use: "create", Short: "create a new endpoint", - Long: "create a new serverless endpoint", + Long: `create a new serverless endpoint. + +requires either --template-id or --hub-id. + +examples: + # create from a template + runpodctl serverless create --template-id --gpu-id "NVIDIA GeForce RTX 4090" + + # create from a hub repo + runpodctl hub search vllm # find the hub id + runpodctl serverless create --hub-id --gpu-id "NVIDIA GeForce RTX 4090"`, Args: cobra.NoArgs, RunE: runCreate, } var ( - createName string - createTemplateID string - createComputeType string - createGpuTypeID string - createGpuCount int - createWorkersMin int - createWorkersMax int + createName string + createTemplateID string + createHubID string + createComputeType string + createGpuTypeID string + createGpuCount int + createWorkersMin int + createWorkersMax int createDataCenterIDs string createNetworkVolumeID string ) func init() { createCmd.Flags().StringVar(&createName, "name", "", "endpoint name") - createCmd.Flags().StringVar(&createTemplateID, "template-id", "", "template id (required)") + createCmd.Flags().StringVar(&createTemplateID, "template-id", "", "template id (required if no --hub-id)") + createCmd.Flags().StringVar(&createHubID, "hub-id", "", "hub listing id (alternative to --template-id)") createCmd.Flags().StringVar(&createComputeType, "compute-type", "GPU", "compute type (GPU or CPU)") createCmd.Flags().StringVar(&createGpuTypeID, "gpu-id", "", "gpu id (from 'runpodctl gpu list')") createCmd.Flags().IntVar(&createGpuCount, "gpu-count", 1, "number of gpus per worker") @@ -41,10 +55,16 @@ func init() { createCmd.Flags().StringVar(&createDataCenterIDs, "data-center-ids", "", "comma-separated list of data center ids") createCmd.Flags().StringVar(&createNetworkVolumeID, "network-volume-id", "", "network volume id to attach") - createCmd.MarkFlagRequired("template-id") //nolint:errcheck } func runCreate(cmd *cobra.Command, args []string) error { + if createTemplateID == "" && createHubID == "" { + return fmt.Errorf("either --template-id or --hub-id is required\n\nuse 'runpodctl hub search ' to find hub repos\nuse 'runpodctl template search ' to find templates") + } + if createTemplateID != "" && createHubID != "" { + return fmt.Errorf("--template-id and --hub-id are mutually exclusive; use one or the other") + } + client, err := api.NewClient() if err != nil { output.Error(err) @@ -64,6 +84,86 @@ func runCreate(cmd *cobra.Command, args []string) error { if strings.Contains(gpuTypeID, ",") { return fmt.Errorf("only one gpu id is supported; use --gpu-count for multiple gpus of the same type") } + + // hub-id path: resolve listing, create via graphql (REST api doesn't support hubReleaseId) + if createHubID != "" { + listing, err := client.GetListing(createHubID) + if err != nil { + output.Error(err) + return fmt.Errorf("failed to get hub listing: %w", err) + } + if listing.ListedRelease == nil { + return fmt.Errorf("hub listing %q has no published release", createHubID) + } + + release := listing.ListedRelease + + // build inline template from the hub release (same as web ui) + var imageName string + if release.Build != nil { + imageName = release.Build.ImageName + } + if imageName == "" { + return fmt.Errorf("hub listing %q has no built image; the release may still be building", createHubID) + } + + containerDisk := 10 + var hubConfig api.HubReleaseConfig + if release.Config != "" { + if err := json.Unmarshal([]byte(release.Config), &hubConfig); err == nil { + if hubConfig.ContainerDiskInGb > 0 { + containerDisk = hubConfig.ContainerDiskInGb + } + } + } + + endpointName := createName + if endpointName == "" { + endpointName = listing.Title + } + + //nolint:gosec + templateName := fmt.Sprintf("%s__template__%s", endpointName, randomString(7)) + + gqlReq := &api.EndpointCreateGQLInput{ + Name: endpointName, + HubReleaseID: release.ID, + Template: &api.EndpointTemplateInput{ + Name: templateName, + ImageName: imageName, + ContainerDiskInGb: containerDisk, + DockerArgs: "", + Env: []*api.PodEnvVar{}, + }, + GpuCount: createGpuCount, + WorkersMin: createWorkersMin, + WorkersMax: createWorkersMax, + } + + // use gpu ids from hub config if not explicitly provided + if gpuTypeID != "" { + gqlReq.GpuIDs = gpuTypeID + } else if hubConfig.GpuIDs != "" { + gqlReq.GpuIDs = hubConfig.GpuIDs + } + if createNetworkVolumeID != "" { + gqlReq.NetworkVolumeID = createNetworkVolumeID + } + if createDataCenterIDs != "" { + gqlReq.Locations = createDataCenterIDs + } + + endpoint, err := client.CreateEndpointGQL(gqlReq) + if err != nil { + output.Error(err) + return fmt.Errorf("failed to create endpoint: %w", err) + } + + format := output.ParseFormat(cmd.Flag("output").Value.String()) + return output.Print(endpoint, &output.Config{Format: format}) + } + + // template-id path: create via REST if gpuTypeID != "" { req.GpuTypeIDs = []string{gpuTypeID} } @@ -85,3 +185,12 @@ func runCreate(cmd *cobra.Command, args []string) error { format := output.ParseFormat(cmd.Flag("output").Value.String()) return output.Print(endpoint, &output.Config{Format: format}) } + +func randomString(n int) string { + const letters = "abcdefghijklmnopqrstuvwxyz0123456789" + b := make([]byte, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] //nolint:gosec + } + return string(b) +} diff --git a/docs/runpodctl.md b/docs/runpodctl.md index 0b8218c..d71fdf6 100644 --- a/docs/runpodctl.md +++ b/docs/runpodctl.md @@ -15,6 +15,7 @@ resources: pod manage gpu pods serverless manage serverless endpoints (alias: sls) template manage templates (alias: tpl) + hub browse the runpod hub model manage model repository network-volume manage network volumes (alias: nv) registry manage container registry auth (alias: reg) @@ -48,6 +49,7 @@ deprecated * [runpodctl datacenter](runpodctl_datacenter.md) - list datacenters * [runpodctl doctor](runpodctl_doctor.md) - diagnose and fix cli issues * [runpodctl gpu](runpodctl_gpu.md) - list available gpu types +* [runpodctl hub](runpodctl_hub.md) - browse the runpod hub * [runpodctl model](runpodctl_model.md) - manage model repository * [runpodctl network-volume](runpodctl_network-volume.md) - manage network volumes * [runpodctl pod](runpodctl_pod.md) - manage gpu pods @@ -61,4 +63,4 @@ deprecated * [runpodctl user](runpodctl_user.md) - show account info * [runpodctl version](runpodctl_version.md) - print the version -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_billing.md b/docs/runpodctl_billing.md index 02e12c6..edfb14a 100644 --- a/docs/runpodctl_billing.md +++ b/docs/runpodctl_billing.md @@ -25,4 +25,4 @@ view billing history for pods, serverless, and network volumes * [runpodctl billing pods](runpodctl_billing_pods.md) - view pod billing history * [runpodctl billing serverless](runpodctl_billing_serverless.md) - view serverless billing history -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_billing_network-volume.md b/docs/runpodctl_billing_network-volume.md index 942bb01..744170b 100644 --- a/docs/runpodctl_billing_network-volume.md +++ b/docs/runpodctl_billing_network-volume.md @@ -29,4 +29,4 @@ runpodctl billing network-volume [flags] * [runpodctl billing](runpodctl_billing.md) - view billing history -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_billing_pods.md b/docs/runpodctl_billing_pods.md index fe79d4e..618af38 100644 --- a/docs/runpodctl_billing_pods.md +++ b/docs/runpodctl_billing_pods.md @@ -32,4 +32,4 @@ runpodctl billing pods [flags] * [runpodctl billing](runpodctl_billing.md) - view billing history -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_billing_serverless.md b/docs/runpodctl_billing_serverless.md index 7f30b17..810aaaf 100644 --- a/docs/runpodctl_billing_serverless.md +++ b/docs/runpodctl_billing_serverless.md @@ -32,4 +32,4 @@ runpodctl billing serverless [flags] * [runpodctl billing](runpodctl_billing.md) - view billing history -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_completion.md b/docs/runpodctl_completion.md index cf98b61..441c497 100644 --- a/docs/runpodctl_completion.md +++ b/docs/runpodctl_completion.md @@ -26,4 +26,4 @@ runpodctl completion [flags] * [runpodctl](runpodctl.md) - cli for runpod.io -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_datacenter.md b/docs/runpodctl_datacenter.md index 9f9799a..00ab6d1 100644 --- a/docs/runpodctl_datacenter.md +++ b/docs/runpodctl_datacenter.md @@ -23,4 +23,4 @@ list datacenters and their gpu availability * [runpodctl](runpodctl.md) - cli for runpod.io * [runpodctl datacenter list](runpodctl_datacenter_list.md) - list all datacenters -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_datacenter_list.md b/docs/runpodctl_datacenter_list.md index f7dc2be..1cc3e88 100644 --- a/docs/runpodctl_datacenter_list.md +++ b/docs/runpodctl_datacenter_list.md @@ -26,4 +26,4 @@ runpodctl datacenter list [flags] * [runpodctl datacenter](runpodctl_datacenter.md) - list datacenters -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_doctor.md b/docs/runpodctl_doctor.md index 5bd3444..aeffea1 100644 --- a/docs/runpodctl_doctor.md +++ b/docs/runpodctl_doctor.md @@ -26,4 +26,4 @@ runpodctl doctor [flags] * [runpodctl](runpodctl.md) - cli for runpod.io -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_gpu.md b/docs/runpodctl_gpu.md index cf23762..99ec0ce 100644 --- a/docs/runpodctl_gpu.md +++ b/docs/runpodctl_gpu.md @@ -23,4 +23,4 @@ list available gpu types and their availability * [runpodctl](runpodctl.md) - cli for runpod.io * [runpodctl gpu list](runpodctl_gpu_list.md) - list available gpu types -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_gpu_list.md b/docs/runpodctl_gpu_list.md index 43e309d..ec4b336 100644 --- a/docs/runpodctl_gpu_list.md +++ b/docs/runpodctl_gpu_list.md @@ -27,4 +27,4 @@ runpodctl gpu list [flags] * [runpodctl gpu](runpodctl_gpu.md) - list available gpu types -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_hub.md b/docs/runpodctl_hub.md new file mode 100644 index 0000000..8faa7de --- /dev/null +++ b/docs/runpodctl_hub.md @@ -0,0 +1,28 @@ +## runpodctl hub + +browse the runpod hub + +### Synopsis + +browse and search the runpod hub for deployable repos + +### Options + +``` + -h, --help help for hub +``` + +### Options inherited from parent commands + +``` + -o, --output string output format (json, yaml) (default "json") +``` + +### SEE ALSO + +* [runpodctl](runpodctl.md) - cli for runpod.io +* [runpodctl hub get](runpodctl_hub_get.md) - get hub repo details +* [runpodctl hub list](runpodctl_hub_list.md) - list hub repos +* [runpodctl hub search](runpodctl_hub_search.md) - search hub repos + +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_hub_get.md b/docs/runpodctl_hub_get.md new file mode 100644 index 0000000..e7703a8 --- /dev/null +++ b/docs/runpodctl_hub_get.md @@ -0,0 +1,33 @@ +## runpodctl hub get + +get hub repo details + +### Synopsis + +get details for a hub repo by id or owner/name. + +examples: + runpodctl hub get clma1kziv00064iog9u6acj6z # by listing id + runpodctl hub get runpod/worker-vllm # by owner/name + +``` +runpodctl hub get [flags] +``` + +### Options + +``` + -h, --help help for get +``` + +### Options inherited from parent commands + +``` + -o, --output string output format (json, yaml) (default "json") +``` + +### SEE ALSO + +* [runpodctl hub](runpodctl_hub.md) - browse the runpod hub + +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_hub_list.md b/docs/runpodctl_hub_list.md new file mode 100644 index 0000000..fed1de2 --- /dev/null +++ b/docs/runpodctl_hub_list.md @@ -0,0 +1,46 @@ +## runpodctl hub list + +list hub repos + +### Synopsis + +list repos from the runpod hub. + +by default shows the top 10 repos ordered by stars. + +examples: + runpodctl hub list # top 10 by stars + runpodctl hub list --type SERVERLESS # only serverless repos + runpodctl hub list --type POD # only pod repos + runpodctl hub list --category ai --limit 20 # filter by category + runpodctl hub list --order-by deploys # order by deploys + runpodctl hub list --owner runpod # filter by repo owner + +``` +runpodctl hub list [flags] +``` + +### Options + +``` + --category string filter by category + -h, --help help for list + --limit int max number of results to return (default 10) + --offset int offset for pagination + --order-by string order by: createdAt, deploys, releasedAt, stars, updatedAt, views (default "stars") + --order-dir string order direction: asc or desc (default "desc") + --owner string filter by repo owner + --type string filter by type: POD or SERVERLESS +``` + +### Options inherited from parent commands + +``` + -o, --output string output format (json, yaml) (default "json") +``` + +### SEE ALSO + +* [runpodctl hub](runpodctl_hub.md) - browse the runpod hub + +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_hub_search.md b/docs/runpodctl_hub_search.md new file mode 100644 index 0000000..44f045f --- /dev/null +++ b/docs/runpodctl_hub_search.md @@ -0,0 +1,41 @@ +## runpodctl hub search + +search hub repos + +### Synopsis + +search for repos in the runpod hub. + +examples: + runpodctl hub search vllm # search for "vllm" + runpodctl hub search whisper --type SERVERLESS # search serverless repos + runpodctl hub search stable-diffusion --limit 5 # limit results + +``` +runpodctl hub search [flags] +``` + +### Options + +``` + --category string filter by category + -h, --help help for search + --limit int max number of results to return (default 10) + --offset int offset for pagination + --order-by string order by: createdAt, deploys, releasedAt, stars, updatedAt, views (default "stars") + --order-dir string order direction: asc or desc (default "desc") + --owner string filter by repo owner + --type string filter by type: POD or SERVERLESS +``` + +### Options inherited from parent commands + +``` + -o, --output string output format (json, yaml) (default "json") +``` + +### SEE ALSO + +* [runpodctl hub](runpodctl_hub.md) - browse the runpod hub + +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_model.md b/docs/runpodctl_model.md index f36b065..ce07474 100644 --- a/docs/runpodctl_model.md +++ b/docs/runpodctl_model.md @@ -25,4 +25,4 @@ manage models in the runpod model repository * [runpodctl model list](runpodctl_model_list.md) - list models * [runpodctl model remove](runpodctl_model_remove.md) - remove a model -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_model_add.md b/docs/runpodctl_model_add.md index 875d64e..bc3c606 100644 --- a/docs/runpodctl_model_add.md +++ b/docs/runpodctl_model_add.md @@ -38,4 +38,4 @@ runpodctl model add [flags] * [runpodctl model](runpodctl_model.md) - manage model repository -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_model_list.md b/docs/runpodctl_model_list.md index d39c680..55965ac 100644 --- a/docs/runpodctl_model_list.md +++ b/docs/runpodctl_model_list.md @@ -29,4 +29,4 @@ runpodctl model list [flags] * [runpodctl model](runpodctl_model.md) - manage model repository -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_model_remove.md b/docs/runpodctl_model_remove.md index 6940cad..775e5b3 100644 --- a/docs/runpodctl_model_remove.md +++ b/docs/runpodctl_model_remove.md @@ -28,4 +28,4 @@ runpodctl model remove [flags] * [runpodctl model](runpodctl_model.md) - manage model repository -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_network-volume.md b/docs/runpodctl_network-volume.md index a006894..1aa0cc0 100644 --- a/docs/runpodctl_network-volume.md +++ b/docs/runpodctl_network-volume.md @@ -27,4 +27,4 @@ manage network volumes on runpod * [runpodctl network-volume list](runpodctl_network-volume_list.md) - list all network volumes * [runpodctl network-volume update](runpodctl_network-volume_update.md) - update a network volume -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_network-volume_create.md b/docs/runpodctl_network-volume_create.md index 9b0ad2e..8885157 100644 --- a/docs/runpodctl_network-volume_create.md +++ b/docs/runpodctl_network-volume_create.md @@ -29,4 +29,4 @@ runpodctl network-volume create [flags] * [runpodctl network-volume](runpodctl_network-volume.md) - manage network volumes -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_network-volume_delete.md b/docs/runpodctl_network-volume_delete.md index 140dd71..37908c1 100644 --- a/docs/runpodctl_network-volume_delete.md +++ b/docs/runpodctl_network-volume_delete.md @@ -26,4 +26,4 @@ runpodctl network-volume delete [flags] * [runpodctl network-volume](runpodctl_network-volume.md) - manage network volumes -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_network-volume_get.md b/docs/runpodctl_network-volume_get.md index f805941..bc410dd 100644 --- a/docs/runpodctl_network-volume_get.md +++ b/docs/runpodctl_network-volume_get.md @@ -26,4 +26,4 @@ runpodctl network-volume get [flags] * [runpodctl network-volume](runpodctl_network-volume.md) - manage network volumes -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_network-volume_list.md b/docs/runpodctl_network-volume_list.md index 63e9dbb..f316737 100644 --- a/docs/runpodctl_network-volume_list.md +++ b/docs/runpodctl_network-volume_list.md @@ -26,4 +26,4 @@ runpodctl network-volume list [flags] * [runpodctl network-volume](runpodctl_network-volume.md) - manage network volumes -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_network-volume_update.md b/docs/runpodctl_network-volume_update.md index 923a279..1b2a2ea 100644 --- a/docs/runpodctl_network-volume_update.md +++ b/docs/runpodctl_network-volume_update.md @@ -28,4 +28,4 @@ runpodctl network-volume update [flags] * [runpodctl network-volume](runpodctl_network-volume.md) - manage network volumes -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_pod.md b/docs/runpodctl_pod.md index f7f3ab0..6db137e 100644 --- a/docs/runpodctl_pod.md +++ b/docs/runpodctl_pod.md @@ -31,4 +31,4 @@ manage gpu pods on runpod * [runpodctl pod stop](runpodctl_pod_stop.md) - stop a running pod * [runpodctl pod update](runpodctl_pod_update.md) - update an existing pod -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_pod_create.md b/docs/runpodctl_pod_create.md index 21a248c..baf674b 100644 --- a/docs/runpodctl_pod_create.md +++ b/docs/runpodctl_pod_create.md @@ -59,4 +59,4 @@ runpodctl pod create [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_pod_delete.md b/docs/runpodctl_pod_delete.md index 05dd527..4a75b97 100644 --- a/docs/runpodctl_pod_delete.md +++ b/docs/runpodctl_pod_delete.md @@ -26,4 +26,4 @@ runpodctl pod delete [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_pod_get.md b/docs/runpodctl_pod_get.md index 32cc691..4cf582b 100644 --- a/docs/runpodctl_pod_get.md +++ b/docs/runpodctl_pod_get.md @@ -28,4 +28,4 @@ runpodctl pod get [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_pod_list.md b/docs/runpodctl_pod_list.md index 8b52a4a..40d387c 100644 --- a/docs/runpodctl_pod_list.md +++ b/docs/runpodctl_pod_list.md @@ -32,4 +32,4 @@ runpodctl pod list [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_pod_reset.md b/docs/runpodctl_pod_reset.md index 7a1432e..b568837 100644 --- a/docs/runpodctl_pod_reset.md +++ b/docs/runpodctl_pod_reset.md @@ -26,4 +26,4 @@ runpodctl pod reset [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_pod_restart.md b/docs/runpodctl_pod_restart.md index 2a6fe17..5e7de87 100644 --- a/docs/runpodctl_pod_restart.md +++ b/docs/runpodctl_pod_restart.md @@ -26,4 +26,4 @@ runpodctl pod restart [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_pod_start.md b/docs/runpodctl_pod_start.md index dbd043f..88acdf2 100644 --- a/docs/runpodctl_pod_start.md +++ b/docs/runpodctl_pod_start.md @@ -26,4 +26,4 @@ runpodctl pod start [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_pod_stop.md b/docs/runpodctl_pod_stop.md index 4ab4f83..0d4fbb3 100644 --- a/docs/runpodctl_pod_stop.md +++ b/docs/runpodctl_pod_stop.md @@ -26,4 +26,4 @@ runpodctl pod stop [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_pod_update.md b/docs/runpodctl_pod_update.md index 1a2e92d..457b67b 100644 --- a/docs/runpodctl_pod_update.md +++ b/docs/runpodctl_pod_update.md @@ -33,4 +33,4 @@ runpodctl pod update [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_receive.md b/docs/runpodctl_receive.md index 113bb93..114941f 100644 --- a/docs/runpodctl_receive.md +++ b/docs/runpodctl_receive.md @@ -26,4 +26,4 @@ runpodctl receive [flags] * [runpodctl](runpodctl.md) - cli for runpod.io -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_registry.md b/docs/runpodctl_registry.md index 1c288c1..9e8f153 100644 --- a/docs/runpodctl_registry.md +++ b/docs/runpodctl_registry.md @@ -26,4 +26,4 @@ manage container registry authentication on runpod * [runpodctl registry get](runpodctl_registry_get.md) - get registry auth details * [runpodctl registry list](runpodctl_registry_list.md) - list all registry auths -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_registry_create.md b/docs/runpodctl_registry_create.md index 22fa944..d9e6e17 100644 --- a/docs/runpodctl_registry_create.md +++ b/docs/runpodctl_registry_create.md @@ -29,4 +29,4 @@ runpodctl registry create [flags] * [runpodctl registry](runpodctl_registry.md) - manage container registry auth -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_registry_delete.md b/docs/runpodctl_registry_delete.md index f8601b9..49d05b8 100644 --- a/docs/runpodctl_registry_delete.md +++ b/docs/runpodctl_registry_delete.md @@ -26,4 +26,4 @@ runpodctl registry delete [flags] * [runpodctl registry](runpodctl_registry.md) - manage container registry auth -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_registry_get.md b/docs/runpodctl_registry_get.md index 1492012..cf05c23 100644 --- a/docs/runpodctl_registry_get.md +++ b/docs/runpodctl_registry_get.md @@ -26,4 +26,4 @@ runpodctl registry get [flags] * [runpodctl registry](runpodctl_registry.md) - manage container registry auth -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_registry_list.md b/docs/runpodctl_registry_list.md index 8aaa07d..c9edb1e 100644 --- a/docs/runpodctl_registry_list.md +++ b/docs/runpodctl_registry_list.md @@ -26,4 +26,4 @@ runpodctl registry list [flags] * [runpodctl registry](runpodctl_registry.md) - manage container registry auth -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_send.md b/docs/runpodctl_send.md index 70b5c64..26d038c 100644 --- a/docs/runpodctl_send.md +++ b/docs/runpodctl_send.md @@ -27,4 +27,4 @@ runpodctl send [flags] * [runpodctl](runpodctl.md) - cli for runpod.io -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_serverless.md b/docs/runpodctl_serverless.md index 427d80f..48c75c0 100644 --- a/docs/runpodctl_serverless.md +++ b/docs/runpodctl_serverless.md @@ -27,4 +27,4 @@ manage serverless endpoints on runpod * [runpodctl serverless list](runpodctl_serverless_list.md) - list all endpoints * [runpodctl serverless update](runpodctl_serverless_update.md) - update an endpoint -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_serverless_create.md b/docs/runpodctl_serverless_create.md index b83f896..09f89a7 100644 --- a/docs/runpodctl_serverless_create.md +++ b/docs/runpodctl_serverless_create.md @@ -4,7 +4,17 @@ create a new endpoint ### Synopsis -create a new serverless endpoint +create a new serverless endpoint. + +requires either --template-id or --hub-id. + +examples: + # create from a template + runpodctl serverless create --template-id --gpu-id "NVIDIA GeForce RTX 4090" + + # create from a hub repo + runpodctl hub search vllm # find the hub id + runpodctl serverless create --hub-id --gpu-id "NVIDIA GeForce RTX 4090" ``` runpodctl serverless create [flags] @@ -18,9 +28,10 @@ runpodctl serverless create [flags] --gpu-count int number of gpus per worker (default 1) --gpu-id string gpu id (from 'runpodctl gpu list') -h, --help help for create + --hub-id string hub listing id (alternative to --template-id) --name string endpoint name --network-volume-id string network volume id to attach - --template-id string template id (required) + --template-id string template id (required if no --hub-id) --workers-max int maximum number of workers (default 3) --workers-min int minimum number of workers ``` @@ -35,4 +46,4 @@ runpodctl serverless create [flags] * [runpodctl serverless](runpodctl_serverless.md) - manage serverless endpoints -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_serverless_delete.md b/docs/runpodctl_serverless_delete.md index 6ca48a7..4fbfda6 100644 --- a/docs/runpodctl_serverless_delete.md +++ b/docs/runpodctl_serverless_delete.md @@ -26,4 +26,4 @@ runpodctl serverless delete [flags] * [runpodctl serverless](runpodctl_serverless.md) - manage serverless endpoints -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_serverless_get.md b/docs/runpodctl_serverless_get.md index 0f89b22..84011a2 100644 --- a/docs/runpodctl_serverless_get.md +++ b/docs/runpodctl_serverless_get.md @@ -28,4 +28,4 @@ runpodctl serverless get [flags] * [runpodctl serverless](runpodctl_serverless.md) - manage serverless endpoints -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_serverless_list.md b/docs/runpodctl_serverless_list.md index bd2725f..e61bd98 100644 --- a/docs/runpodctl_serverless_list.md +++ b/docs/runpodctl_serverless_list.md @@ -28,4 +28,4 @@ runpodctl serverless list [flags] * [runpodctl serverless](runpodctl_serverless.md) - manage serverless endpoints -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_serverless_update.md b/docs/runpodctl_serverless_update.md index f5f8b37..0e1a6c9 100644 --- a/docs/runpodctl_serverless_update.md +++ b/docs/runpodctl_serverless_update.md @@ -32,4 +32,4 @@ runpodctl serverless update [flags] * [runpodctl serverless](runpodctl_serverless.md) - manage serverless endpoints -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_ssh.md b/docs/runpodctl_ssh.md index 1737bfe..d7a4fc1 100644 --- a/docs/runpodctl_ssh.md +++ b/docs/runpodctl_ssh.md @@ -25,4 +25,4 @@ manage ssh keys and show ssh info for pods. uses the api key from RUNPOD_API_KEY * [runpodctl ssh info](runpodctl_ssh_info.md) - show ssh info for a pod * [runpodctl ssh list-keys](runpodctl_ssh_list-keys.md) - list all ssh keys -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_ssh_add-key.md b/docs/runpodctl_ssh_add-key.md index feb3d52..6d69329 100644 --- a/docs/runpodctl_ssh_add-key.md +++ b/docs/runpodctl_ssh_add-key.md @@ -28,4 +28,4 @@ runpodctl ssh add-key [flags] * [runpodctl ssh](runpodctl_ssh.md) - manage ssh keys and connections -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_ssh_info.md b/docs/runpodctl_ssh_info.md index 5ab8d59..8fce651 100644 --- a/docs/runpodctl_ssh_info.md +++ b/docs/runpodctl_ssh_info.md @@ -27,4 +27,4 @@ runpodctl ssh info [flags] * [runpodctl ssh](runpodctl_ssh.md) - manage ssh keys and connections -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_ssh_list-keys.md b/docs/runpodctl_ssh_list-keys.md index 4523c9e..715600e 100644 --- a/docs/runpodctl_ssh_list-keys.md +++ b/docs/runpodctl_ssh_list-keys.md @@ -26,4 +26,4 @@ runpodctl ssh list-keys [flags] * [runpodctl ssh](runpodctl_ssh.md) - manage ssh keys and connections -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_template.md b/docs/runpodctl_template.md index e495589..3fa678f 100644 --- a/docs/runpodctl_template.md +++ b/docs/runpodctl_template.md @@ -28,4 +28,4 @@ manage templates on runpod * [runpodctl template search](runpodctl_template_search.md) - search templates * [runpodctl template update](runpodctl_template_update.md) - update a template -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_template_create.md b/docs/runpodctl_template_create.md index 7dca12d..d5c42df 100644 --- a/docs/runpodctl_template_create.md +++ b/docs/runpodctl_template_create.md @@ -37,4 +37,4 @@ runpodctl template create [flags] * [runpodctl template](runpodctl_template.md) - manage templates -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_template_delete.md b/docs/runpodctl_template_delete.md index 6c219a1..246d26d 100644 --- a/docs/runpodctl_template_delete.md +++ b/docs/runpodctl_template_delete.md @@ -26,4 +26,4 @@ runpodctl template delete [flags] * [runpodctl template](runpodctl_template.md) - manage templates -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_template_get.md b/docs/runpodctl_template_get.md index 614917e..ccbcbef 100644 --- a/docs/runpodctl_template_get.md +++ b/docs/runpodctl_template_get.md @@ -26,4 +26,4 @@ runpodctl template get [flags] * [runpodctl template](runpodctl_template.md) - manage templates -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_template_list.md b/docs/runpodctl_template_list.md index a4f41a1..936b73d 100644 --- a/docs/runpodctl_template_list.md +++ b/docs/runpodctl_template_list.md @@ -41,4 +41,4 @@ runpodctl template list [flags] * [runpodctl template](runpodctl_template.md) - manage templates -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_template_search.md b/docs/runpodctl_template_search.md index b21de71..df07148 100644 --- a/docs/runpodctl_template_search.md +++ b/docs/runpodctl_template_search.md @@ -37,4 +37,4 @@ runpodctl template search [flags] * [runpodctl template](runpodctl_template.md) - manage templates -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_template_update.md b/docs/runpodctl_template_update.md index 329eb54..6d74818 100644 --- a/docs/runpodctl_template_update.md +++ b/docs/runpodctl_template_update.md @@ -31,4 +31,4 @@ runpodctl template update [flags] * [runpodctl template](runpodctl_template.md) - manage templates -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_update.md b/docs/runpodctl_update.md index d9bec13..ef9c938 100644 --- a/docs/runpodctl_update.md +++ b/docs/runpodctl_update.md @@ -26,4 +26,4 @@ runpodctl update [flags] * [runpodctl](runpodctl.md) - cli for runpod.io -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_user.md b/docs/runpodctl_user.md index d93694f..b34fbd7 100644 --- a/docs/runpodctl_user.md +++ b/docs/runpodctl_user.md @@ -26,4 +26,4 @@ runpodctl user [flags] * [runpodctl](runpodctl.md) - cli for runpod.io -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/docs/runpodctl_version.md b/docs/runpodctl_version.md index 27ba418..a0c4e75 100644 --- a/docs/runpodctl_version.md +++ b/docs/runpodctl_version.md @@ -22,4 +22,4 @@ runpodctl version [flags] * [runpodctl](runpodctl.md) - cli for runpod.io -###### Auto generated by spf13/cobra on 23-Mar-2026 +###### Auto generated by spf13/cobra on 25-Mar-2026 diff --git a/e2e/cli_test.go b/e2e/cli_test.go index 3c7543e..7d5126e 100644 --- a/e2e/cli_test.go +++ b/e2e/cli_test.go @@ -455,6 +455,296 @@ func TestCLI_PodCreateCPU(t *testing.T) { } } +// --- Hub tests --- + +func TestCLI_HubList(t *testing.T) { + stdout, stderr, err := runCLI("hub", "list") + if err != nil { + t.Fatalf("failed to run hub list: %v\nstderr: %s", err, stderr) + } + + var listings []map[string]interface{} + if err := json.Unmarshal([]byte(stdout), &listings); err != nil { + t.Fatalf("output is not valid json: %v\noutput: %s", err, stdout) + } + + if len(listings) == 0 { + t.Error("expected at least one hub listing") + } + + t.Logf("found %d hub listings", len(listings)) +} + +func TestCLI_HubListTypeServerless(t *testing.T) { + stdout, stderr, err := runCLI("hub", "list", "--type", "SERVERLESS", "--limit", "5") + if err != nil { + t.Fatalf("failed to run hub list --type SERVERLESS: %v\nstderr: %s", err, stderr) + } + + var listings []map[string]interface{} + if err := json.Unmarshal([]byte(stdout), &listings); err != nil { + t.Fatalf("output is not valid json: %v\noutput: %s", err, stdout) + } + + for _, l := range listings { + lType, _ := l["type"].(string) + if lType != "SERVERLESS" { + t.Errorf("expected type SERVERLESS, got %q for %v", lType, l["repoName"]) + } + } + + if len(listings) == 0 { + t.Error("expected at least one serverless hub listing") + } + t.Logf("found %d serverless hub listings (limited to 5)", len(listings)) +} + +func TestCLI_HubListTypePod(t *testing.T) { + stdout, stderr, err := runCLI("hub", "list", "--type", "POD", "--limit", "5") + if err != nil { + t.Fatalf("failed to run hub list --type POD: %v\nstderr: %s", err, stderr) + } + + var listings []map[string]interface{} + if err := json.Unmarshal([]byte(stdout), &listings); err != nil { + t.Fatalf("output is not valid json: %v\noutput: %s", err, stdout) + } + + for _, l := range listings { + lType, _ := l["type"].(string) + if lType != "POD" { + t.Errorf("expected type POD, got %q for %v", lType, l["repoName"]) + } + } + + t.Logf("found %d pod hub listings (limited to 5)", len(listings)) +} + +func TestCLI_HubListPagination(t *testing.T) { + stdout1, _, err := runCLI("hub", "list", "--limit", "3") + if err != nil { + t.Skip("skipping pagination test - can't get first page") + } + + stdout2, _, err := runCLI("hub", "list", "--limit", "3", "--offset", "3") + if err != nil { + t.Skip("skipping pagination test - can't get second page") + } + + var page1, page2 []map[string]interface{} + json.Unmarshal([]byte(stdout1), &page1) + json.Unmarshal([]byte(stdout2), &page2) + + if len(page1) == 0 || len(page2) == 0 { + t.Skip("skipping pagination test - not enough listings") + } + + page2FirstID := page2[0]["id"] + for _, l := range page1 { + if l["id"] == page2FirstID { + t.Error("pagination not working - same listing on both pages") + } + } + + t.Logf("pagination works: page1=%d listings, page2=%d listings", len(page1), len(page2)) +} + +func TestCLI_HubSearch(t *testing.T) { + stdout, stderr, err := runCLI("hub", "search", "vllm") + if err != nil { + t.Fatalf("failed to search hub: %v\nstderr: %s", err, stderr) + } + + var listings []map[string]interface{} + if err := json.Unmarshal([]byte(stdout), &listings); err != nil { + t.Fatalf("output is not valid json: %v\noutput: %s", err, stdout) + } + + if len(listings) == 0 { + t.Error("expected at least one result for 'vllm'") + } + + // verify at least one result is the official vllm worker + found := false + for _, l := range listings { + repoName, _ := l["repoName"].(string) + if repoName == "worker-vllm" { + found = true + break + } + } + if !found { + t.Error("expected worker-vllm in search results") + } + + t.Logf("found %d hub repos matching 'vllm'", len(listings)) +} + +func TestCLI_HubSearchNoResults(t *testing.T) { + stdout, stderr, err := runCLI("hub", "search", "zzz_nonexistent_repo_12345") + if err != nil { + t.Fatalf("failed to search hub: %v\nstderr: %s", err, stderr) + } + + // should print "no hub repos found" to stdout (not JSON) + if !strings.Contains(stdout, "no hub repos found") { + t.Errorf("expected 'no hub repos found' message, got: %s", stdout) + } +} + +func TestCLI_HubGetByID(t *testing.T) { + // first get a listing id from list + stdout, _, err := runCLI("hub", "list", "--limit", "1") + if err != nil { + t.Skip("skipping hub get test - can't list hub") + } + + var listings []map[string]interface{} + if err := json.Unmarshal([]byte(stdout), &listings); err != nil || len(listings) == 0 { + t.Skip("skipping hub get test - no listings") + } + + listingID := listings[0]["id"].(string) + stdout, stderr, err := runCLI("hub", "get", listingID) + if err != nil { + t.Fatalf("failed to get hub listing %s: %v\nstderr: %s", listingID, err, stderr) + } + + var listing map[string]interface{} + if err := json.Unmarshal([]byte(stdout), &listing); err != nil { + t.Fatalf("output is not valid json: %v\noutput: %s", err, stdout) + } + + if listing["id"] != listingID { + t.Errorf("expected listing id %s, got %v", listingID, listing["id"]) + } + + // get should include listedRelease + if listing["listedRelease"] == nil { + t.Error("expected listedRelease in get response") + } + + t.Logf("got hub listing: %v (type=%v)", listing["title"], listing["type"]) +} + +func TestCLI_HubGetByOwnerName(t *testing.T) { + stdout, stderr, err := runCLI("hub", "get", "runpod-workers/worker-vllm") + if err != nil { + t.Fatalf("failed to get hub listing by owner/name: %v\nstderr: %s", err, stderr) + } + + var listing map[string]interface{} + if err := json.Unmarshal([]byte(stdout), &listing); err != nil { + t.Fatalf("output is not valid json: %v\noutput: %s", err, stdout) + } + + if listing["repoOwner"] != "runpod-workers" { + t.Errorf("expected repoOwner 'runpod-workers', got %v", listing["repoOwner"]) + } + if listing["repoName"] != "worker-vllm" { + t.Errorf("expected repoName 'worker-vllm', got %v", listing["repoName"]) + } + + t.Logf("got hub listing by owner/name: %v", listing["title"]) +} + +func TestCLI_HubGetBuildImage(t *testing.T) { + // verify build.imageName is returned in get response + stdout, stderr, err := runCLI("hub", "get", "runpod-workers/worker-vllm") + if err != nil { + t.Fatalf("failed to get hub listing: %v\nstderr: %s", err, stderr) + } + + var listing map[string]interface{} + if err := json.Unmarshal([]byte(stdout), &listing); err != nil { + t.Fatalf("output is not valid json: %v", err) + } + + release, ok := listing["listedRelease"].(map[string]interface{}) + if !ok || release == nil { + t.Fatal("expected listedRelease in response") + } + + build, ok := release["build"].(map[string]interface{}) + if !ok || build == nil { + t.Fatal("expected build in listedRelease") + } + + imageName, ok := build["imageName"].(string) + if !ok || imageName == "" { + t.Fatal("expected imageName in build") + } + + t.Logf("build image: %s", imageName) +} + +// --- Serverless create from hub --- + +func TestCLI_ServerlessCreateRequiresTemplateOrHub(t *testing.T) { + _, stderr, err := runCLI("serverless", "create", "--gpu-id", "NVIDIA GeForce RTX 4090") + if err == nil { + t.Fatal("expected error when creating serverless without template or hub") + } + if !strings.Contains(stderr, "either --template-id or --hub-id is required") { + t.Errorf("expected helpful error, got: %s", stderr) + } +} + +func TestCLI_ServerlessCreateMutuallyExclusive(t *testing.T) { + _, stderr, err := runCLI("serverless", "create", "--template-id", "x", "--hub-id", "y") + if err == nil { + t.Fatal("expected error when both --template-id and --hub-id are provided") + } + if !strings.Contains(stderr, "mutually exclusive") { + t.Errorf("expected mutually exclusive error, got: %s", stderr) + } +} + +func TestCLI_ServerlessCreateFromHub(t *testing.T) { + // create a serverless endpoint from the vllm hub listing + name := "e2e-test-hub-" + time.Now().Format("20060102150405") + stdout, stderr, err := runCLI("serverless", "create", + "--hub-id", "cm8h09d9n000008jvh2rqdsmb", // vllm listing + "--name", name, + "--workers-max", "1", + ) + if err != nil { + t.Fatalf("failed to create serverless endpoint from hub: %v\nstderr: %s", err, stderr) + } + + var endpoint map[string]interface{} + if err := json.Unmarshal([]byte(stdout), &endpoint); err != nil { + t.Fatalf("output is not valid json: %v\noutput: %s", err, stdout) + } + + endpointID, ok := endpoint["id"].(string) + if !ok || strings.TrimSpace(endpointID) == "" { + t.Fatal("expected endpoint id in response") + } + + // cleanup immediately + t.Cleanup(func() { + _, _, err := runCLI("serverless", "delete", endpointID) + if err != nil { + t.Logf("warning: failed to delete test endpoint %s: %v", endpointID, err) + } else { + t.Logf("cleaned up endpoint %s", endpointID) + } + }) + + if endpoint["name"] != name { + t.Errorf("expected name %q, got %v", name, endpoint["name"]) + } + + // verify gpu ids were pulled from hub config + gpuIDs, _ := endpoint["gpuIds"].(string) + if gpuIDs == "" { + t.Error("expected gpuIds from hub config") + } + + t.Logf("created endpoint %s from hub (gpuIds=%s)", endpointID, gpuIDs) +} + func TestCLI_EndpointList(t *testing.T) { stdout, stderr, err := runCLI("serverless", "list") if err != nil { @@ -1439,6 +1729,9 @@ func TestCLI_HelpCoverage(t *testing.T) { {"project", "dev"}, {"project", "build"}, {"project", "deploy"}, + {"hub", "list"}, + {"hub", "search"}, + {"hub", "get"}, {"serverless", "create"}, {"serverless", "update"}, {"serverless", "delete"}, diff --git a/internal/api/endpoints.go b/internal/api/endpoints.go index c66f085..ea7d6e9 100644 --- a/internal/api/endpoints.go +++ b/internal/api/endpoints.go @@ -32,7 +32,8 @@ type EndpointListResponse struct { // EndpointCreateRequest is the request to create an endpoint type EndpointCreateRequest struct { Name string `json:"name,omitempty"` - TemplateID string `json:"templateId"` + TemplateID string `json:"templateId,omitempty"` + HubReleaseID string `json:"hubReleaseId,omitempty"` ComputeType string `json:"computeType,omitempty"` GpuTypeIDs []string `json:"gpuTypeIds,omitempty"` GpuCount int `json:"gpuCount,omitempty"` @@ -141,3 +142,80 @@ func (c *Client) DeleteEndpoint(endpointID string) error { _, err := c.Delete("/endpoints/" + endpointID) return err } + +// EndpointCreateGQLInput is the input for creating an endpoint via GraphQL +// Used when hubReleaseId is needed (REST API doesn't support it) +type EndpointCreateGQLInput struct { + Name string `json:"name"` + HubReleaseID string `json:"hubReleaseId,omitempty"` + TemplateID string `json:"templateId,omitempty"` + Template *EndpointTemplateInput `json:"template,omitempty"` + GpuIDs string `json:"gpuIds,omitempty"` + GpuCount int `json:"gpuCount,omitempty"` + WorkersMin int `json:"workersMin,omitempty"` + WorkersMax int `json:"workersMax,omitempty"` + Locations string `json:"locations,omitempty"` + NetworkVolumeID string `json:"networkVolumeId,omitempty"` +} + +// EndpointTemplateInput is the inline template for endpoint creation via GraphQL +type EndpointTemplateInput struct { + Name string `json:"name"` + ImageName string `json:"imageName,omitempty"` + ContainerDiskInGb int `json:"containerDiskInGb"` + DockerArgs string `json:"dockerArgs"` + Env []*PodEnvVar `json:"env"` +} + +// CreateEndpointGQL creates an endpoint via GraphQL (saveEndpoint mutation) +func (c *Client) CreateEndpointGQL(req *EndpointCreateGQLInput) (*Endpoint, error) { + query := ` + mutation SaveEndpoint($input: EndpointInput!) { + saveEndpoint(input: $input) { + id + name + gpuIds + networkVolumeId + locations + idleTimeout + scalerType + scalerValue + workersMin + workersMax + gpuCount + } + } + ` + + variables := map[string]interface{}{ + "input": req, + } + + data, err := c.graphqlRequest(query, variables) + if err != nil { + return nil, err + } + + var resp struct { + Data struct { + SaveEndpoint *Endpoint `json:"saveEndpoint"` + } `json:"data"` + Errors []struct { + Message string `json:"message"` + } `json:"errors"` + } + + if err := json.Unmarshal(data, &resp); err != nil { + return nil, fmt.Errorf("failed to parse response: %w", err) + } + + if len(resp.Errors) > 0 { + return nil, fmt.Errorf("graphql error: %s", resp.Errors[0].Message) + } + + if resp.Data.SaveEndpoint == nil { + return nil, fmt.Errorf("endpoint creation returned nil response") + } + + return resp.Data.SaveEndpoint, nil +} diff --git a/internal/api/hub.go b/internal/api/hub.go new file mode 100644 index 0000000..f6a5493 --- /dev/null +++ b/internal/api/hub.go @@ -0,0 +1,301 @@ +package api + +import ( + "encoding/json" + "fmt" + "strings" +) + +// Listing represents a hub listing (repo) +type Listing struct { + ID string `json:"id"` + Title string `json:"title,omitempty"` + RepoName string `json:"repoName"` + RepoOwner string `json:"repoOwner"` + RepoOwnerAvatarUrl string `json:"repoOwnerAvatarUrl,omitempty"` + Description string `json:"description,omitempty"` + Category string `json:"category,omitempty"` + Type string `json:"type"` + Tags []string `json:"tags,omitempty"` + Stars int `json:"stars"` + Views int `json:"views"` + Deploys int `json:"deploys"` + OpenIssues int `json:"openIssues"` + Watchers int `json:"watchers"` + Language string `json:"language,omitempty"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` + ListedRelease *HubRelease `json:"listedRelease,omitempty"` +} + +// HubRelease represents a release of a hub listing +type HubRelease struct { + ID string `json:"id"` + Name string `json:"name"` + TagName string `json:"tagName"` + Body string `json:"body,omitempty"` + Readme string `json:"readme,omitempty"` + Branch string `json:"branch,omitempty"` + License string `json:"license,omitempty"` + Config string `json:"config,omitempty"` + Deploys int `json:"deploys"` + IconUrl string `json:"iconUrl,omitempty"` + AuthorName string `json:"authorName,omitempty"` + CreatedAt string `json:"createdAt"` + ReleasedAt string `json:"releasedAt"` + UpdatedAt string `json:"updatedAt"` + Build *GitBuild `json:"build,omitempty"` +} + +// GitBuild represents a build from a hub release +type GitBuild struct { + ImageName string `json:"imageName,omitempty"` +} + +// HubReleaseConfig is the parsed config from a hub release +type HubReleaseConfig struct { + ContainerDiskInGb int `json:"containerDiskInGb,omitempty"` + GpuIDs string `json:"gpuIds,omitempty"` + GpuCount int `json:"gpuCount,omitempty"` + Env []HubReleaseConfigEnv `json:"env,omitempty"` +} + +// HubReleaseConfigEnv is an env var entry in the hub release config +type HubReleaseConfigEnv struct { + Key string `json:"key"` + Input *HubReleaseConfigEnvInput `json:"input,omitempty"` +} + +// HubReleaseConfigEnvInput describes the env var input metadata +type HubReleaseConfigEnvInput struct { + Default interface{} `json:"default,omitempty"` +} + +// ListingsOptions for listing/searching hub entries +type ListingsOptions struct { + SearchQuery string + Category string + Limit int + Offset int + OrderBy string + OrderDirection string + Owner string + Type string // client-side filter: POD or SERVERLESS +} + +// ListListings returns hub listings with optional filtering +func (c *Client) ListListings(opts *ListingsOptions) ([]Listing, error) { + query := ` + query Listings($input: ListingsInput!) { + listings(input: $input) { + id + title + repoName + repoOwner + description + category + type + tags + stars + views + deploys + language + createdAt + updatedAt + } + } + ` + + input := map[string]interface{}{} + if opts != nil { + if opts.SearchQuery != "" { + input["searchQuery"] = opts.SearchQuery + } + if opts.Category != "" { + input["category"] = opts.Category + } + if opts.Limit > 0 { + input["limit"] = opts.Limit + } + if opts.Offset > 0 { + input["offset"] = opts.Offset + } + if opts.OrderBy != "" { + input["orderBy"] = opts.OrderBy + } + if opts.OrderDirection != "" { + input["orderDirection"] = opts.OrderDirection + } + if opts.Owner != "" { + input["owner"] = opts.Owner + } + } + + variables := map[string]interface{}{ + "input": input, + } + + data, err := c.graphqlRequest(query, variables) + if err != nil { + return nil, err + } + + var resp struct { + Data struct { + Listings []Listing `json:"listings"` + } `json:"data"` + Errors []struct { + Message string `json:"message"` + } `json:"errors"` + } + + if err := json.Unmarshal(data, &resp); err != nil { + return nil, fmt.Errorf("failed to parse response: %w", err) + } + + if len(resp.Errors) > 0 { + return nil, fmt.Errorf("graphql error: %s", resp.Errors[0].Message) + } + + listings := resp.Data.Listings + + // client-side type filter (ListingsInput has no type field) + if opts != nil && opts.Type != "" { + filterType := strings.ToUpper(opts.Type) + var filtered []Listing + for _, l := range listings { + if strings.ToUpper(l.Type) == filterType { + filtered = append(filtered, l) + } + } + listings = filtered + } + + return listings, nil +} + +const listingFullFields = ` + id + title + repoName + repoOwner + repoOwnerAvatarUrl + description + category + type + tags + stars + views + deploys + openIssues + watchers + language + createdAt + updatedAt + listedRelease { + id + name + tagName + body + readme + branch + license + config + deploys + iconUrl + authorName + createdAt + releasedAt + updatedAt + build { + imageName + } + } +` + +// GetListing returns a single hub listing by ID +func (c *Client) GetListing(listingID string) (*Listing, error) { + query := fmt.Sprintf(` + query GetListing($id: String!) { + listing(id: $id) { + %s + } + } + `, listingFullFields) + + variables := map[string]interface{}{ + "id": listingID, + } + + data, err := c.graphqlRequest(query, variables) + if err != nil { + return nil, err + } + + var resp struct { + Data struct { + Listing *Listing `json:"listing"` + } `json:"data"` + Errors []struct { + Message string `json:"message"` + } `json:"errors"` + } + + if err := json.Unmarshal(data, &resp); err != nil { + return nil, fmt.Errorf("failed to parse response: %w", err) + } + + if len(resp.Errors) > 0 { + return nil, fmt.Errorf("graphql error: %s", resp.Errors[0].Message) + } + + if resp.Data.Listing == nil { + return nil, fmt.Errorf("hub listing not found: %s", listingID) + } + + return resp.Data.Listing, nil +} + +// GetListingFromRepo returns a hub listing by repo owner and name +func (c *Client) GetListingFromRepo(owner, name string) (*Listing, error) { + query := fmt.Sprintf(` + query GetListingFromRepo($repoOwner: String!, $repoName: String!) { + listingFromRepo(repoOwner: $repoOwner, repoName: $repoName) { + %s + } + } + `, listingFullFields) + + variables := map[string]interface{}{ + "repoOwner": owner, + "repoName": name, + } + + data, err := c.graphqlRequest(query, variables) + if err != nil { + return nil, err + } + + var resp struct { + Data struct { + Listing *Listing `json:"listingFromRepo"` + } `json:"data"` + Errors []struct { + Message string `json:"message"` + } `json:"errors"` + } + + if err := json.Unmarshal(data, &resp); err != nil { + return nil, fmt.Errorf("failed to parse response: %w", err) + } + + if len(resp.Errors) > 0 { + return nil, fmt.Errorf("graphql error: %s", resp.Errors[0].Message) + } + + if resp.Data.Listing == nil { + return nil, fmt.Errorf("hub listing not found: %s/%s", owner, name) + } + + return resp.Data.Listing, nil +} From 177fc0e00bb1f69e86b78796de9691ff4fc665a4 Mon Sep 17 00:00:00 2001 From: Tim Pietrusky Date: Wed, 25 Mar 2026 16:39:21 +0100 Subject: [PATCH 2/3] test: add evals for deploying vllm and comfyui from hub adds two eval scenarios to test whether an agent can discover and deploy serverless endpoints from the runpod hub using the new hub commands and --hub-id flag. --- evals/deploy-comfyui-serverless.eval.md | 23 +++++++++++++++++++++++ evals/deploy-vllm-serverless.eval.md | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 evals/deploy-comfyui-serverless.eval.md create mode 100644 evals/deploy-vllm-serverless.eval.md diff --git a/evals/deploy-comfyui-serverless.eval.md b/evals/deploy-comfyui-serverless.eval.md new file mode 100644 index 0000000..bebda60 --- /dev/null +++ b/evals/deploy-comfyui-serverless.eval.md @@ -0,0 +1,23 @@ +# Deploy ComfyUI as a serverless endpoint + +## Prompt + +I want to run ComfyUI as a serverless endpoint on Runpod. + +## Expected behavior + +The agent should: + +1. Search the hub for ComfyUI: `runpodctl hub search comfyui` or `runpodctl hub search ComfyUI` +2. Identify the `runpod-workers/worker-comfyui` listing and note its hub ID +3. Optionally get details: `runpodctl hub get ` or `runpodctl hub get runpod-workers/worker-comfyui` +4. Create a serverless endpoint using the hub ID: `runpodctl serverless create --hub-id --name ""` +5. Verify the endpoint was created successfully +6. Clean up the endpoint after verification: `runpodctl serverless delete ` + +## Assertions + +- Agent uses `runpodctl hub search` or `runpodctl hub list` to discover the ComfyUI listing +- Agent uses `runpodctl serverless create --hub-id` (not `--template-id`) to deploy +- Agent cleans up the created endpoint with `runpodctl serverless delete` +- Agent does NOT try to create a template manually diff --git a/evals/deploy-vllm-serverless.eval.md b/evals/deploy-vllm-serverless.eval.md new file mode 100644 index 0000000..b716e62 --- /dev/null +++ b/evals/deploy-vllm-serverless.eval.md @@ -0,0 +1,23 @@ +# Deploy vLLM as a serverless endpoint + +## Prompt + +I want to run vLLM as a serverless endpoint on Runpod. + +## Expected behavior + +The agent should: + +1. Search the hub for vLLM: `runpodctl hub search vllm` +2. Identify the official `runpod-workers/worker-vllm` listing and note its hub ID +3. Optionally get details: `runpodctl hub get ` or `runpodctl hub get runpod-workers/worker-vllm` +4. Create a serverless endpoint using the hub ID: `runpodctl serverless create --hub-id --name ""` +5. Verify the endpoint was created successfully +6. Clean up the endpoint after verification: `runpodctl serverless delete ` + +## Assertions + +- Agent uses `runpodctl hub search` or `runpodctl hub list` to discover the vLLM listing +- Agent uses `runpodctl serverless create --hub-id` (not `--template-id`) to deploy +- Agent cleans up the created endpoint with `runpodctl serverless delete` +- Agent does NOT try to create a template manually From a41be3cc9d956d640bc20ab0b349dacf7b70f68f Mon Sep 17 00:00:00 2001 From: Tim Pietrusky Date: Thu, 26 Mar 2026 13:54:16 +0100 Subject: [PATCH 3/3] fix: skip volume fields for serverless templates, add tests field to hub release - serverless template creation via REST fails because the api rejects volumeInGb on serverless templates even when set to 0; skip sending volume fields when --serverless is set - include tests field in hub release get response so agents can use the pre-built test payloads to call deployed endpoints --- cmd/template/create.go | 8 ++++++-- docs/runpodctl.md | 2 +- docs/runpodctl_billing.md | 2 +- docs/runpodctl_billing_network-volume.md | 2 +- docs/runpodctl_billing_pods.md | 2 +- docs/runpodctl_billing_serverless.md | 2 +- docs/runpodctl_completion.md | 2 +- docs/runpodctl_datacenter.md | 2 +- docs/runpodctl_datacenter_list.md | 2 +- docs/runpodctl_doctor.md | 2 +- docs/runpodctl_gpu.md | 2 +- docs/runpodctl_gpu_list.md | 2 +- docs/runpodctl_hub.md | 2 +- docs/runpodctl_hub_get.md | 2 +- docs/runpodctl_hub_list.md | 2 +- docs/runpodctl_hub_search.md | 2 +- docs/runpodctl_model.md | 2 +- docs/runpodctl_model_add.md | 2 +- docs/runpodctl_model_list.md | 2 +- docs/runpodctl_model_remove.md | 2 +- docs/runpodctl_network-volume.md | 2 +- docs/runpodctl_network-volume_create.md | 2 +- docs/runpodctl_network-volume_delete.md | 2 +- docs/runpodctl_network-volume_get.md | 2 +- docs/runpodctl_network-volume_list.md | 2 +- docs/runpodctl_network-volume_update.md | 2 +- docs/runpodctl_pod.md | 2 +- docs/runpodctl_pod_create.md | 2 +- docs/runpodctl_pod_delete.md | 2 +- docs/runpodctl_pod_get.md | 2 +- docs/runpodctl_pod_list.md | 2 +- docs/runpodctl_pod_reset.md | 2 +- docs/runpodctl_pod_restart.md | 2 +- docs/runpodctl_pod_start.md | 2 +- docs/runpodctl_pod_stop.md | 2 +- docs/runpodctl_pod_update.md | 2 +- docs/runpodctl_receive.md | 2 +- docs/runpodctl_registry.md | 2 +- docs/runpodctl_registry_create.md | 2 +- docs/runpodctl_registry_delete.md | 2 +- docs/runpodctl_registry_get.md | 2 +- docs/runpodctl_registry_list.md | 2 +- docs/runpodctl_send.md | 2 +- docs/runpodctl_serverless.md | 2 +- docs/runpodctl_serverless_create.md | 2 +- docs/runpodctl_serverless_delete.md | 2 +- docs/runpodctl_serverless_get.md | 2 +- docs/runpodctl_serverless_list.md | 2 +- docs/runpodctl_serverless_update.md | 2 +- docs/runpodctl_ssh.md | 2 +- docs/runpodctl_ssh_add-key.md | 2 +- docs/runpodctl_ssh_info.md | 2 +- docs/runpodctl_ssh_list-keys.md | 2 +- docs/runpodctl_template.md | 2 +- docs/runpodctl_template_create.md | 2 +- docs/runpodctl_template_delete.md | 2 +- docs/runpodctl_template_get.md | 2 +- docs/runpodctl_template_list.md | 2 +- docs/runpodctl_template_search.md | 2 +- docs/runpodctl_template_update.md | 2 +- docs/runpodctl_update.md | 2 +- docs/runpodctl_user.md | 2 +- docs/runpodctl_version.md | 2 +- internal/api/hub.go | 2 ++ 64 files changed, 70 insertions(+), 64 deletions(-) diff --git a/cmd/template/create.go b/cmd/template/create.go index 2251b64..9c24c72 100644 --- a/cmd/template/create.go +++ b/cmd/template/create.go @@ -62,11 +62,15 @@ func runCreate(cmd *cobra.Command, args []string) error { ImageName: createImageName, IsServerless: createIsServerless, ContainerDiskInGb: createContainerDiskInGb, - VolumeInGb: createVolumeInGb, - VolumeMountPath: createVolumeMountPath, Readme: createReadme, } + // serverless templates do not support volume fields + if !createIsServerless { + req.VolumeInGb = createVolumeInGb + req.VolumeMountPath = createVolumeMountPath + } + if createPorts != "" { req.Ports = strings.Split(createPorts, ",") } diff --git a/docs/runpodctl.md b/docs/runpodctl.md index d71fdf6..cd42c18 100644 --- a/docs/runpodctl.md +++ b/docs/runpodctl.md @@ -63,4 +63,4 @@ deprecated * [runpodctl user](runpodctl_user.md) - show account info * [runpodctl version](runpodctl_version.md) - print the version -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_billing.md b/docs/runpodctl_billing.md index edfb14a..641d158 100644 --- a/docs/runpodctl_billing.md +++ b/docs/runpodctl_billing.md @@ -25,4 +25,4 @@ view billing history for pods, serverless, and network volumes * [runpodctl billing pods](runpodctl_billing_pods.md) - view pod billing history * [runpodctl billing serverless](runpodctl_billing_serverless.md) - view serverless billing history -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_billing_network-volume.md b/docs/runpodctl_billing_network-volume.md index 744170b..d667c2d 100644 --- a/docs/runpodctl_billing_network-volume.md +++ b/docs/runpodctl_billing_network-volume.md @@ -29,4 +29,4 @@ runpodctl billing network-volume [flags] * [runpodctl billing](runpodctl_billing.md) - view billing history -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_billing_pods.md b/docs/runpodctl_billing_pods.md index 618af38..c0121e8 100644 --- a/docs/runpodctl_billing_pods.md +++ b/docs/runpodctl_billing_pods.md @@ -32,4 +32,4 @@ runpodctl billing pods [flags] * [runpodctl billing](runpodctl_billing.md) - view billing history -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_billing_serverless.md b/docs/runpodctl_billing_serverless.md index 810aaaf..317f023 100644 --- a/docs/runpodctl_billing_serverless.md +++ b/docs/runpodctl_billing_serverless.md @@ -32,4 +32,4 @@ runpodctl billing serverless [flags] * [runpodctl billing](runpodctl_billing.md) - view billing history -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_completion.md b/docs/runpodctl_completion.md index 441c497..971895f 100644 --- a/docs/runpodctl_completion.md +++ b/docs/runpodctl_completion.md @@ -26,4 +26,4 @@ runpodctl completion [flags] * [runpodctl](runpodctl.md) - cli for runpod.io -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_datacenter.md b/docs/runpodctl_datacenter.md index 00ab6d1..62150cb 100644 --- a/docs/runpodctl_datacenter.md +++ b/docs/runpodctl_datacenter.md @@ -23,4 +23,4 @@ list datacenters and their gpu availability * [runpodctl](runpodctl.md) - cli for runpod.io * [runpodctl datacenter list](runpodctl_datacenter_list.md) - list all datacenters -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_datacenter_list.md b/docs/runpodctl_datacenter_list.md index 1cc3e88..daa1762 100644 --- a/docs/runpodctl_datacenter_list.md +++ b/docs/runpodctl_datacenter_list.md @@ -26,4 +26,4 @@ runpodctl datacenter list [flags] * [runpodctl datacenter](runpodctl_datacenter.md) - list datacenters -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_doctor.md b/docs/runpodctl_doctor.md index aeffea1..f6a76d9 100644 --- a/docs/runpodctl_doctor.md +++ b/docs/runpodctl_doctor.md @@ -26,4 +26,4 @@ runpodctl doctor [flags] * [runpodctl](runpodctl.md) - cli for runpod.io -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_gpu.md b/docs/runpodctl_gpu.md index 99ec0ce..59bb590 100644 --- a/docs/runpodctl_gpu.md +++ b/docs/runpodctl_gpu.md @@ -23,4 +23,4 @@ list available gpu types and their availability * [runpodctl](runpodctl.md) - cli for runpod.io * [runpodctl gpu list](runpodctl_gpu_list.md) - list available gpu types -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_gpu_list.md b/docs/runpodctl_gpu_list.md index ec4b336..e9412d5 100644 --- a/docs/runpodctl_gpu_list.md +++ b/docs/runpodctl_gpu_list.md @@ -27,4 +27,4 @@ runpodctl gpu list [flags] * [runpodctl gpu](runpodctl_gpu.md) - list available gpu types -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_hub.md b/docs/runpodctl_hub.md index 8faa7de..4ceec3b 100644 --- a/docs/runpodctl_hub.md +++ b/docs/runpodctl_hub.md @@ -25,4 +25,4 @@ browse and search the runpod hub for deployable repos * [runpodctl hub list](runpodctl_hub_list.md) - list hub repos * [runpodctl hub search](runpodctl_hub_search.md) - search hub repos -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_hub_get.md b/docs/runpodctl_hub_get.md index e7703a8..810fd4b 100644 --- a/docs/runpodctl_hub_get.md +++ b/docs/runpodctl_hub_get.md @@ -30,4 +30,4 @@ runpodctl hub get [flags] * [runpodctl hub](runpodctl_hub.md) - browse the runpod hub -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_hub_list.md b/docs/runpodctl_hub_list.md index fed1de2..c9677d9 100644 --- a/docs/runpodctl_hub_list.md +++ b/docs/runpodctl_hub_list.md @@ -43,4 +43,4 @@ runpodctl hub list [flags] * [runpodctl hub](runpodctl_hub.md) - browse the runpod hub -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_hub_search.md b/docs/runpodctl_hub_search.md index 44f045f..c75a05b 100644 --- a/docs/runpodctl_hub_search.md +++ b/docs/runpodctl_hub_search.md @@ -38,4 +38,4 @@ runpodctl hub search [flags] * [runpodctl hub](runpodctl_hub.md) - browse the runpod hub -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_model.md b/docs/runpodctl_model.md index ce07474..12c2593 100644 --- a/docs/runpodctl_model.md +++ b/docs/runpodctl_model.md @@ -25,4 +25,4 @@ manage models in the runpod model repository * [runpodctl model list](runpodctl_model_list.md) - list models * [runpodctl model remove](runpodctl_model_remove.md) - remove a model -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_model_add.md b/docs/runpodctl_model_add.md index bc3c606..23173a6 100644 --- a/docs/runpodctl_model_add.md +++ b/docs/runpodctl_model_add.md @@ -38,4 +38,4 @@ runpodctl model add [flags] * [runpodctl model](runpodctl_model.md) - manage model repository -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_model_list.md b/docs/runpodctl_model_list.md index 55965ac..1052cab 100644 --- a/docs/runpodctl_model_list.md +++ b/docs/runpodctl_model_list.md @@ -29,4 +29,4 @@ runpodctl model list [flags] * [runpodctl model](runpodctl_model.md) - manage model repository -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_model_remove.md b/docs/runpodctl_model_remove.md index 775e5b3..96fd755 100644 --- a/docs/runpodctl_model_remove.md +++ b/docs/runpodctl_model_remove.md @@ -28,4 +28,4 @@ runpodctl model remove [flags] * [runpodctl model](runpodctl_model.md) - manage model repository -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_network-volume.md b/docs/runpodctl_network-volume.md index 1aa0cc0..fe3a646 100644 --- a/docs/runpodctl_network-volume.md +++ b/docs/runpodctl_network-volume.md @@ -27,4 +27,4 @@ manage network volumes on runpod * [runpodctl network-volume list](runpodctl_network-volume_list.md) - list all network volumes * [runpodctl network-volume update](runpodctl_network-volume_update.md) - update a network volume -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_network-volume_create.md b/docs/runpodctl_network-volume_create.md index 8885157..8eec315 100644 --- a/docs/runpodctl_network-volume_create.md +++ b/docs/runpodctl_network-volume_create.md @@ -29,4 +29,4 @@ runpodctl network-volume create [flags] * [runpodctl network-volume](runpodctl_network-volume.md) - manage network volumes -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_network-volume_delete.md b/docs/runpodctl_network-volume_delete.md index 37908c1..9c00167 100644 --- a/docs/runpodctl_network-volume_delete.md +++ b/docs/runpodctl_network-volume_delete.md @@ -26,4 +26,4 @@ runpodctl network-volume delete [flags] * [runpodctl network-volume](runpodctl_network-volume.md) - manage network volumes -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_network-volume_get.md b/docs/runpodctl_network-volume_get.md index bc410dd..66e2926 100644 --- a/docs/runpodctl_network-volume_get.md +++ b/docs/runpodctl_network-volume_get.md @@ -26,4 +26,4 @@ runpodctl network-volume get [flags] * [runpodctl network-volume](runpodctl_network-volume.md) - manage network volumes -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_network-volume_list.md b/docs/runpodctl_network-volume_list.md index f316737..b4992a5 100644 --- a/docs/runpodctl_network-volume_list.md +++ b/docs/runpodctl_network-volume_list.md @@ -26,4 +26,4 @@ runpodctl network-volume list [flags] * [runpodctl network-volume](runpodctl_network-volume.md) - manage network volumes -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_network-volume_update.md b/docs/runpodctl_network-volume_update.md index 1b2a2ea..cacf16f 100644 --- a/docs/runpodctl_network-volume_update.md +++ b/docs/runpodctl_network-volume_update.md @@ -28,4 +28,4 @@ runpodctl network-volume update [flags] * [runpodctl network-volume](runpodctl_network-volume.md) - manage network volumes -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_pod.md b/docs/runpodctl_pod.md index 6db137e..6c33696 100644 --- a/docs/runpodctl_pod.md +++ b/docs/runpodctl_pod.md @@ -31,4 +31,4 @@ manage gpu pods on runpod * [runpodctl pod stop](runpodctl_pod_stop.md) - stop a running pod * [runpodctl pod update](runpodctl_pod_update.md) - update an existing pod -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_pod_create.md b/docs/runpodctl_pod_create.md index baf674b..8086abc 100644 --- a/docs/runpodctl_pod_create.md +++ b/docs/runpodctl_pod_create.md @@ -59,4 +59,4 @@ runpodctl pod create [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_pod_delete.md b/docs/runpodctl_pod_delete.md index 4a75b97..1c7a37d 100644 --- a/docs/runpodctl_pod_delete.md +++ b/docs/runpodctl_pod_delete.md @@ -26,4 +26,4 @@ runpodctl pod delete [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_pod_get.md b/docs/runpodctl_pod_get.md index 4cf582b..ae3bba3 100644 --- a/docs/runpodctl_pod_get.md +++ b/docs/runpodctl_pod_get.md @@ -28,4 +28,4 @@ runpodctl pod get [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_pod_list.md b/docs/runpodctl_pod_list.md index 40d387c..3ebc1b9 100644 --- a/docs/runpodctl_pod_list.md +++ b/docs/runpodctl_pod_list.md @@ -32,4 +32,4 @@ runpodctl pod list [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_pod_reset.md b/docs/runpodctl_pod_reset.md index b568837..c6a44ea 100644 --- a/docs/runpodctl_pod_reset.md +++ b/docs/runpodctl_pod_reset.md @@ -26,4 +26,4 @@ runpodctl pod reset [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_pod_restart.md b/docs/runpodctl_pod_restart.md index 5e7de87..565e6f8 100644 --- a/docs/runpodctl_pod_restart.md +++ b/docs/runpodctl_pod_restart.md @@ -26,4 +26,4 @@ runpodctl pod restart [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_pod_start.md b/docs/runpodctl_pod_start.md index 88acdf2..ec72e42 100644 --- a/docs/runpodctl_pod_start.md +++ b/docs/runpodctl_pod_start.md @@ -26,4 +26,4 @@ runpodctl pod start [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_pod_stop.md b/docs/runpodctl_pod_stop.md index 0d4fbb3..00ebe83 100644 --- a/docs/runpodctl_pod_stop.md +++ b/docs/runpodctl_pod_stop.md @@ -26,4 +26,4 @@ runpodctl pod stop [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_pod_update.md b/docs/runpodctl_pod_update.md index 457b67b..87ea646 100644 --- a/docs/runpodctl_pod_update.md +++ b/docs/runpodctl_pod_update.md @@ -33,4 +33,4 @@ runpodctl pod update [flags] * [runpodctl pod](runpodctl_pod.md) - manage gpu pods -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_receive.md b/docs/runpodctl_receive.md index 114941f..76cf7ee 100644 --- a/docs/runpodctl_receive.md +++ b/docs/runpodctl_receive.md @@ -26,4 +26,4 @@ runpodctl receive [flags] * [runpodctl](runpodctl.md) - cli for runpod.io -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_registry.md b/docs/runpodctl_registry.md index 9e8f153..31a96d2 100644 --- a/docs/runpodctl_registry.md +++ b/docs/runpodctl_registry.md @@ -26,4 +26,4 @@ manage container registry authentication on runpod * [runpodctl registry get](runpodctl_registry_get.md) - get registry auth details * [runpodctl registry list](runpodctl_registry_list.md) - list all registry auths -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_registry_create.md b/docs/runpodctl_registry_create.md index d9e6e17..b28027b 100644 --- a/docs/runpodctl_registry_create.md +++ b/docs/runpodctl_registry_create.md @@ -29,4 +29,4 @@ runpodctl registry create [flags] * [runpodctl registry](runpodctl_registry.md) - manage container registry auth -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_registry_delete.md b/docs/runpodctl_registry_delete.md index 49d05b8..bf45b92 100644 --- a/docs/runpodctl_registry_delete.md +++ b/docs/runpodctl_registry_delete.md @@ -26,4 +26,4 @@ runpodctl registry delete [flags] * [runpodctl registry](runpodctl_registry.md) - manage container registry auth -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_registry_get.md b/docs/runpodctl_registry_get.md index cf05c23..bb2744b 100644 --- a/docs/runpodctl_registry_get.md +++ b/docs/runpodctl_registry_get.md @@ -26,4 +26,4 @@ runpodctl registry get [flags] * [runpodctl registry](runpodctl_registry.md) - manage container registry auth -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_registry_list.md b/docs/runpodctl_registry_list.md index c9edb1e..83194eb 100644 --- a/docs/runpodctl_registry_list.md +++ b/docs/runpodctl_registry_list.md @@ -26,4 +26,4 @@ runpodctl registry list [flags] * [runpodctl registry](runpodctl_registry.md) - manage container registry auth -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_send.md b/docs/runpodctl_send.md index 26d038c..18bfa81 100644 --- a/docs/runpodctl_send.md +++ b/docs/runpodctl_send.md @@ -27,4 +27,4 @@ runpodctl send [flags] * [runpodctl](runpodctl.md) - cli for runpod.io -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_serverless.md b/docs/runpodctl_serverless.md index 48c75c0..4dbf928 100644 --- a/docs/runpodctl_serverless.md +++ b/docs/runpodctl_serverless.md @@ -27,4 +27,4 @@ manage serverless endpoints on runpod * [runpodctl serverless list](runpodctl_serverless_list.md) - list all endpoints * [runpodctl serverless update](runpodctl_serverless_update.md) - update an endpoint -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_serverless_create.md b/docs/runpodctl_serverless_create.md index 09f89a7..ccbf920 100644 --- a/docs/runpodctl_serverless_create.md +++ b/docs/runpodctl_serverless_create.md @@ -46,4 +46,4 @@ runpodctl serverless create [flags] * [runpodctl serverless](runpodctl_serverless.md) - manage serverless endpoints -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_serverless_delete.md b/docs/runpodctl_serverless_delete.md index 4fbfda6..7d9f6ee 100644 --- a/docs/runpodctl_serverless_delete.md +++ b/docs/runpodctl_serverless_delete.md @@ -26,4 +26,4 @@ runpodctl serverless delete [flags] * [runpodctl serverless](runpodctl_serverless.md) - manage serverless endpoints -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_serverless_get.md b/docs/runpodctl_serverless_get.md index 84011a2..5531128 100644 --- a/docs/runpodctl_serverless_get.md +++ b/docs/runpodctl_serverless_get.md @@ -28,4 +28,4 @@ runpodctl serverless get [flags] * [runpodctl serverless](runpodctl_serverless.md) - manage serverless endpoints -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_serverless_list.md b/docs/runpodctl_serverless_list.md index e61bd98..28bd671 100644 --- a/docs/runpodctl_serverless_list.md +++ b/docs/runpodctl_serverless_list.md @@ -28,4 +28,4 @@ runpodctl serverless list [flags] * [runpodctl serverless](runpodctl_serverless.md) - manage serverless endpoints -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_serverless_update.md b/docs/runpodctl_serverless_update.md index 0e1a6c9..00f32e1 100644 --- a/docs/runpodctl_serverless_update.md +++ b/docs/runpodctl_serverless_update.md @@ -32,4 +32,4 @@ runpodctl serverless update [flags] * [runpodctl serverless](runpodctl_serverless.md) - manage serverless endpoints -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_ssh.md b/docs/runpodctl_ssh.md index d7a4fc1..72f39db 100644 --- a/docs/runpodctl_ssh.md +++ b/docs/runpodctl_ssh.md @@ -25,4 +25,4 @@ manage ssh keys and show ssh info for pods. uses the api key from RUNPOD_API_KEY * [runpodctl ssh info](runpodctl_ssh_info.md) - show ssh info for a pod * [runpodctl ssh list-keys](runpodctl_ssh_list-keys.md) - list all ssh keys -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_ssh_add-key.md b/docs/runpodctl_ssh_add-key.md index 6d69329..398cb9d 100644 --- a/docs/runpodctl_ssh_add-key.md +++ b/docs/runpodctl_ssh_add-key.md @@ -28,4 +28,4 @@ runpodctl ssh add-key [flags] * [runpodctl ssh](runpodctl_ssh.md) - manage ssh keys and connections -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_ssh_info.md b/docs/runpodctl_ssh_info.md index 8fce651..fb06867 100644 --- a/docs/runpodctl_ssh_info.md +++ b/docs/runpodctl_ssh_info.md @@ -27,4 +27,4 @@ runpodctl ssh info [flags] * [runpodctl ssh](runpodctl_ssh.md) - manage ssh keys and connections -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_ssh_list-keys.md b/docs/runpodctl_ssh_list-keys.md index 715600e..9ef6f57 100644 --- a/docs/runpodctl_ssh_list-keys.md +++ b/docs/runpodctl_ssh_list-keys.md @@ -26,4 +26,4 @@ runpodctl ssh list-keys [flags] * [runpodctl ssh](runpodctl_ssh.md) - manage ssh keys and connections -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_template.md b/docs/runpodctl_template.md index 3fa678f..eddd9c6 100644 --- a/docs/runpodctl_template.md +++ b/docs/runpodctl_template.md @@ -28,4 +28,4 @@ manage templates on runpod * [runpodctl template search](runpodctl_template_search.md) - search templates * [runpodctl template update](runpodctl_template_update.md) - update a template -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_template_create.md b/docs/runpodctl_template_create.md index d5c42df..45bd364 100644 --- a/docs/runpodctl_template_create.md +++ b/docs/runpodctl_template_create.md @@ -37,4 +37,4 @@ runpodctl template create [flags] * [runpodctl template](runpodctl_template.md) - manage templates -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_template_delete.md b/docs/runpodctl_template_delete.md index 246d26d..022d647 100644 --- a/docs/runpodctl_template_delete.md +++ b/docs/runpodctl_template_delete.md @@ -26,4 +26,4 @@ runpodctl template delete [flags] * [runpodctl template](runpodctl_template.md) - manage templates -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_template_get.md b/docs/runpodctl_template_get.md index ccbcbef..a3dca66 100644 --- a/docs/runpodctl_template_get.md +++ b/docs/runpodctl_template_get.md @@ -26,4 +26,4 @@ runpodctl template get [flags] * [runpodctl template](runpodctl_template.md) - manage templates -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_template_list.md b/docs/runpodctl_template_list.md index 936b73d..abe8c85 100644 --- a/docs/runpodctl_template_list.md +++ b/docs/runpodctl_template_list.md @@ -41,4 +41,4 @@ runpodctl template list [flags] * [runpodctl template](runpodctl_template.md) - manage templates -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_template_search.md b/docs/runpodctl_template_search.md index df07148..d693b6b 100644 --- a/docs/runpodctl_template_search.md +++ b/docs/runpodctl_template_search.md @@ -37,4 +37,4 @@ runpodctl template search [flags] * [runpodctl template](runpodctl_template.md) - manage templates -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_template_update.md b/docs/runpodctl_template_update.md index 6d74818..f75b175 100644 --- a/docs/runpodctl_template_update.md +++ b/docs/runpodctl_template_update.md @@ -31,4 +31,4 @@ runpodctl template update [flags] * [runpodctl template](runpodctl_template.md) - manage templates -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_update.md b/docs/runpodctl_update.md index ef9c938..cc1dc3d 100644 --- a/docs/runpodctl_update.md +++ b/docs/runpodctl_update.md @@ -26,4 +26,4 @@ runpodctl update [flags] * [runpodctl](runpodctl.md) - cli for runpod.io -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_user.md b/docs/runpodctl_user.md index b34fbd7..ad8ab85 100644 --- a/docs/runpodctl_user.md +++ b/docs/runpodctl_user.md @@ -26,4 +26,4 @@ runpodctl user [flags] * [runpodctl](runpodctl.md) - cli for runpod.io -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/docs/runpodctl_version.md b/docs/runpodctl_version.md index a0c4e75..068eda6 100644 --- a/docs/runpodctl_version.md +++ b/docs/runpodctl_version.md @@ -22,4 +22,4 @@ runpodctl version [flags] * [runpodctl](runpodctl.md) - cli for runpod.io -###### Auto generated by spf13/cobra on 25-Mar-2026 +###### Auto generated by spf13/cobra on 26-Mar-2026 diff --git a/internal/api/hub.go b/internal/api/hub.go index f6a5493..ac8b67a 100644 --- a/internal/api/hub.go +++ b/internal/api/hub.go @@ -45,6 +45,7 @@ type HubRelease struct { ReleasedAt string `json:"releasedAt"` UpdatedAt string `json:"updatedAt"` Build *GitBuild `json:"build,omitempty"` + Tests string `json:"tests,omitempty"` } // GitBuild represents a build from a hub release @@ -210,6 +211,7 @@ const listingFullFields = ` build { imageName } + tests } `