diff --git a/internal/cmd/sandbox_box.go b/internal/cmd/sandbox_box.go index d5f8826..0e64dac 100644 --- a/internal/cmd/sandbox_box.go +++ b/internal/cmd/sandbox_box.go @@ -46,6 +46,21 @@ type sandboxCreateInput struct { ProxyConfig string } +var sandboxBoxDetailRender = structured.PropertyList{ + Properties: []structured.Property{ + {Label: "Name", Template: "{{.Name}}"}, + {Label: "ID", Template: "{{.ID}}"}, + {Label: "Status", Template: "{{.Status}}"}, + {Label: "Size", Template: "{{.SizeClass}}"}, + {Label: "VCPUs", Template: "{{formatCount .Vcpus}}"}, + {Label: "Memory", Template: "{{formatBytesOrDash .MemBytes}}"}, + {Label: "Rootfs", Template: "{{formatBytesOrDash .FsCapacityBytes}}"}, + {Label: "Snapshot", Template: "{{shortID .SnapshotID}}"}, + {Label: "Idle TTL", Template: "{{formatCount .IdleTtlSeconds}}s"}, + {Label: "Created", Template: "{{formatTime .CreatedAt}}"}, + }, +} + var sandboxCreateCommand = structured.Command[*sandboxCreateInput]{ Use: "create ", Short: "Create a sandbox VM from a snapshot", @@ -137,14 +152,7 @@ Examples: return resp, nil }, - Render: structured.Template(`Name: {{.Name}} -Status: {{.Status}} -VCPUs: {{formatCount .Vcpus}} -Memory: {{formatBytesOrDash .MemBytes}} -Rootfs: {{formatBytesOrDash .FsCapacityBytes}} -Snapshot: {{shortID .SnapshotID}} -Created: {{formatTime .CreatedAt}} -`), + Render: sandboxBoxDetailRender, } var sandboxListCommand = structured.Command[struct{}]{ @@ -194,14 +202,7 @@ var sandboxGetCommand = structured.Command[struct{}]{ return resp, nil }, - Render: structured.Template(`Name: {{.Name}} -Status: {{.Status}} -VCPUs: {{formatCount .Vcpus}} -Memory: {{formatBytesOrDash .MemBytes}} -Rootfs: {{formatBytesOrDash .FsCapacityBytes}} -Snapshot: {{shortID .SnapshotID}} -Created: {{formatTime .CreatedAt}} -`), + Render: sandboxBoxDetailRender, } type sandboxUpdateInput struct { @@ -273,14 +274,7 @@ for the proxy config JSON format.`, return resp, nil }, - Render: structured.Template(`Name: {{.Name}} -Status: {{.Status}} -VCPUs: {{formatCount .Vcpus}} -Memory: {{formatBytesOrDash .MemBytes}} -Rootfs: {{formatBytesOrDash .FsCapacityBytes}} -Snapshot: {{shortID .SnapshotID}} -Created: {{formatTime .CreatedAt}} -`), + Render: sandboxBoxDetailRender, } var sandboxDeleteCommand = structured.Command[struct{}]{ diff --git a/internal/cmd/sandbox_test.go b/internal/cmd/sandbox_test.go index ed19a75..91c2fd0 100644 --- a/internal/cmd/sandbox_test.go +++ b/internal/cmd/sandbox_test.go @@ -1,10 +1,14 @@ package cmd import ( + "bytes" "encoding/json" "os" "path/filepath" "testing" + + langsmith "github.com/langchain-ai/langsmith-go" + "github.com/stretchr/testify/require" ) // ==================== parseByteSize ==================== @@ -184,6 +188,80 @@ func TestSandboxCreateCmd_SizeFlags(t *testing.T) { } } +func TestSandboxBoxDetailRenderSupportsSDKResponseTypes(t *testing.T) { + tests := []struct { + name string + model any + wantID string + wantTTL string + }{ + { + name: "new response", + model: langsmith.SandboxBoxNewResponse{ + ID: "box-new", + Name: "new-vm", + Status: "running", + SizeClass: "small", + Vcpus: 2, + MemBytes: 512 * 1024 * 1024, + FsCapacityBytes: 4 * 1024 * 1024 * 1024, + SnapshotID: "1234567890abcdef", + IdleTtlSeconds: 900, + CreatedAt: "2026-05-11T12:00:00Z", + }, + wantID: "ID: box-new", + wantTTL: "Idle TTL: 900s", + }, + { + name: "get response", + model: langsmith.SandboxBoxGetResponse{ + ID: "box-get", + Name: "get-vm", + Status: "running", + SizeClass: "medium", + Vcpus: 4, + MemBytes: 1024 * 1024 * 1024, + FsCapacityBytes: 8 * 1024 * 1024 * 1024, + SnapshotID: "abcdef1234567890", + IdleTtlSeconds: 1800, + CreatedAt: "2026-05-11T12:00:00Z", + }, + wantID: "ID: box-get", + wantTTL: "Idle TTL: 1800s", + }, + { + name: "update response", + model: langsmith.SandboxBoxUpdateResponse{ + ID: "box-update", + Name: "update-vm", + Status: "stopped", + SizeClass: "large", + Vcpus: 8, + MemBytes: 2 * 1024 * 1024 * 1024, + FsCapacityBytes: 16 * 1024 * 1024 * 1024, + SnapshotID: "fedcba0987654321", + IdleTtlSeconds: 3600, + CreatedAt: "2026-05-11T12:00:00Z", + }, + wantID: "ID: box-update", + wantTTL: "Idle TTL: 3600s", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var out bytes.Buffer + + err := sandboxBoxDetailRender.RenderText(&out, tc.model) + + require.NoError(t, err) + require.Contains(t, out.String(), tc.wantID) + require.Contains(t, out.String(), "Size:") + require.Contains(t, out.String(), tc.wantTTL) + }) + } +} + func TestSandboxUpdateCmd_SizeFlags(t *testing.T) { cmd := sandboxUpdateCommand.Cobra() for _, name := range []string{"memory", "rootfs-capacity"} {