package commands import ( "context" "fmt" "time" "github.com/rathi/agentikube/internal/kube" "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" ) func NewCreateCmd() *cobra.Command { var provider string var apiKey string cmd := &cobra.Command{ Use: "create ", Short: "Create a new sandbox for an agent", Long: "Creates a Secret and SandboxClaim for the given handle, then waits for it to be ready.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() handle := args[0] cfg, err := loadConfig(cmd) if err != nil { return err } client, err := kube.NewClient() if err != nil { return fmt.Errorf("connecting to cluster: %w", err) } ns := cfg.Namespace name := "sandbox-" + handle // Create the secret with provider credentials secret := &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "v1", "kind": "Secret", "metadata": map[string]interface{}{ "name": name, "namespace": ns, }, "stringData": map[string]interface{}{ "PROVIDER": provider, "PROVIDER_KEY": apiKey, "USER_NAME": handle, }, }, } secretGVR := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"} _, err = client.Dynamic().Resource(secretGVR).Namespace(ns).Create(ctx, secret, metav1.CreateOptions{}) if err != nil { return fmt.Errorf("creating secret %q: %w", name, err) } fmt.Printf("[ok] secret %q created\n", name) // Create the SandboxClaim claim := &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "agentsandbox.dev/v1", "kind": "SandboxClaim", "metadata": map[string]interface{}{ "name": name, "namespace": ns, }, "spec": map[string]interface{}{ "templateRef": map[string]interface{}{ "name": "sandbox-template", }, "secretRef": map[string]interface{}{ "name": name, }, }, }, } claimGVR := schema.GroupVersionResource{ Group: "agentsandbox.dev", Version: "v1", Resource: "sandboxclaims", } _, err = client.Dynamic().Resource(claimGVR).Namespace(ns).Create(ctx, claim, metav1.CreateOptions{}) if err != nil { return fmt.Errorf("creating SandboxClaim %q: %w", name, err) } fmt.Printf("[ok] SandboxClaim %q created\n", name) // Wait for the sandbox to become ready (3 min timeout) fmt.Println("waiting for sandbox to be ready...") waitCtx, cancel := context.WithTimeout(ctx, 3*time.Minute) defer cancel() if err := client.WaitForReady(waitCtx, ns, "sandboxclaims", name); err != nil { return fmt.Errorf("waiting for sandbox: %w", err) } fmt.Printf("\nsandbox %q is ready\n", handle) fmt.Printf(" name: %s\n", name) fmt.Printf(" namespace: %s\n", ns) fmt.Printf(" ssh: agentikube ssh %s\n", handle) return nil }, } cmd.Flags().StringVar(&provider, "provider", "", "LLM provider name (env: SANDBOX_LLM_PROVIDER)") cmd.Flags().StringVar(&apiKey, "api-key", "", "LLM provider API key (env: SANDBOX_API_KEY)") return cmd }