diff --git a/internal/cli/resources.go b/internal/cli/resources.go index e54f758b..8c311938 100644 --- a/internal/cli/resources.go +++ b/internal/cli/resources.go @@ -36,11 +36,11 @@ func newResourcesCmd() *cobra.Command { Short: "Show how much of this machine tracebloc may use", Long: `Shows, in plain terms, how much of this machine tracebloc may use: - • This machine — the CPU and memory the cluster can schedule - • tracebloc uses — the ceiling a single training run may use right now + • Your secure environment — the CPU and memory it can schedule + • Each training run — the per-run ceiling every run may use (cluster-wide) -No Kubernetes concepts, no YAML — one number for the machine and one for -tracebloc's share of it. +No Kubernetes concepts, no YAML — one number for your environment and one for +each training run's share of it. Raise the share with ` + "`tracebloc resources set`" + `. Run with --verbose for the per-node breakdown and the raw values. @@ -125,16 +125,16 @@ func renderResources(ctx context.Context, p *ui.Printer, target *clusterTarget) train.HasGPU = false } - p.Section("This machine") if nodeErr != nil { - p.Field("capacity", "unavailable") - p.Hintf(" couldn't read node capacity: %v", nodeErr) + p.Stat("Your secure environment is equipped with:", "unavailable") + p.Hintf(" couldn't read capacity: %v", nodeErr) } else { - p.Field("capacity", machineLine(machine)) + p.Stat("Your secure environment is equipped with:", machineLine(machine)) } - - p.Section("tracebloc uses") - p.Field("per training run", trainingLine(train)) + // The per-run ceiling is cluster-wide — jobs-manager stamps it on EVERY + // training run (there is no per-run override today). perRunSize is the same + // "CPU · mem" string `resources set` shows; the label carries the "up to". + p.Stat("A training run is allocated up to:", perRunSize(train)) if p.Verbose() { p.Section("Details") @@ -151,7 +151,13 @@ func renderResources(ctx context.Context, p *ui.Printer, target *clusterTarget) } p.Newline() - p.Hintf("Raise tracebloc's share with `tracebloc resources set` (run it on a terminal for a guided walkthrough).") + // Match the home screen's launcher resolution so the hint reads `tb …` on a + // real install (where the `tb` alias exists) and `tracebloc …` otherwise. + cmd := invokedName() + if tbAliasAvailable() { + cmd = binTB + } + p.Hintf("Do you want to change the allocation? Run `%s resources set` (guided walkthrough on a terminal).", cmd) return nil } @@ -165,15 +171,6 @@ func machineLine(m resources.Machine) string { return line } -// trainingLine renders the per-run ceiling: "up to 2 CPU · 8 GiB" (+ GPU). -func trainingLine(t resources.Training) string { - line := "up to " + resources.FormatCPU(t.CPU) + " · " + resources.FormatMem(t.Mem) - if t.HasGPU { - line += " · " + resources.FormatGPU(t.GPUName, t.GPU) - } - return line -} - // firstNonEmptyEnv returns the first present, non-empty value among keys. func firstNonEmptyEnv(env map[string]string, keys ...string) string { for _, k := range keys { diff --git a/internal/cli/resources_test.go b/internal/cli/resources_test.go index a045c324..1dbbfc93 100644 --- a/internal/cli/resources_test.go +++ b/internal/cli/resources_test.go @@ -72,7 +72,7 @@ func TestRenderResources_ShowsMachineAndTrainingCeiling(t *testing.T) { t.Fatalf("renderResources: %v", err) } out := buf.String() - for _, want := range []string{"This machine", "8 CPU · 32 GiB", "tracebloc uses", "up to 4 CPU · 16 GiB"} { + for _, want := range []string{"Your secure environment is equipped with:", "8 CPU · 32 GiB", "A training run is allocated up to:", "4 CPU · 16 GiB"} { if !strings.Contains(out, want) { t.Errorf("missing %q in:\n%s", want, out) } @@ -93,7 +93,8 @@ func TestRenderResources_ChartDefaultWhenEnvUnset(t *testing.T) { if err := renderResources(context.Background(), ui.New(&buf, ui.WithColor(false)), resTarget(cs)); err != nil { t.Fatalf("renderResources: %v", err) } - if !strings.Contains(buf.String(), "up to 2 CPU · 8 GiB") { + if !strings.Contains(buf.String(), "A training run is allocated up to:") || + !strings.Contains(buf.String(), "2 CPU · 8 GiB") { t.Errorf("want chart-default ceiling 2 CPU · 8 GiB:\n%s", buf.String()) } } diff --git a/internal/ui/ui.go b/internal/ui/ui.go index 22ddac6d..211311db 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -245,6 +245,13 @@ func (p *Printer) Field(label, value string) { p.out(" %s %s\n", p.paint(fmt.Sprintf("%-14s", label+":"), color.Faint), value) } +// Stat prints an aligned "label value" row with a dimmed, fixed-width label, +// so a short block of them lines up. Unlike Field's compact 14-col key, Stat +// fits full-sentence labels (e.g. the resources view's two lines). +func (p *Printer) Stat(label, value string) { + p.out(" %s %s\n", p.paint(fmt.Sprintf("%-42s", label), color.Faint), value) +} + // Action prints an imperative instruction row — a bold verb label and its value, // with no trailing colon: " Open https://…". Used for the device-flow // sign-in steps (Open the URL / Enter the code), where the label is a thing to