Skip to content

Add -o/--output flag to openshell sandbox list #1421

@benoitf

Description

@benoitf

Problem Statement

openshell sandbox list only outputs a human-readable table (or single-field --ids/--names modes). Scripts and CI pipelines that need to parse sandbox metadata (id, name, labels, phase) must scrape table output, which is fragile and breaks when column widths or formatting change.

Provider profile commands already support -o json and -o yaml (added in #1170), but sandbox list does not, creating an inconsistency in the CLI surface.

Proposed Design

Add -o/--output flag to openshell sandbox list accepting table (default), json, and yaml values, matching the existing provider profile pattern.

CLI interface:

openshell sandbox list -o json
openshell sandbox list -o yaml
openshell sandbox list --output json --selector team=backend

JSON output structure (array of objects, pretty-printed):

[
  {
    "id": "abc123",
    "name": "my-sandbox",
    "labels": { "team": "backend", "env": "dev" },
    "created_at": "2026-05-18 14:30:00",
    "phase": "Ready",
    "current_policy_version": 3
  }
]

YAML output structure:

- id: abc123
  name: my-sandbox
  labels:
    team: backend
    env: dev
  created_at: '2026-05-18 14:30:00'
  phase: Ready
  current_policy_version: 3

Design details:

  • Reuse the existing ProviderProfileOutput enum by renaming it to a shared OutputFormat (Table/Yaml/Json) to avoid duplication.
  • -o json/-o yaml conflicts with --ids and --names via clap's conflicts_with_all.
  • Empty sandbox list emits [] (JSON) or []\n (YAML) instead of "No sandboxes found." for clean programmatic parsing.
  • Fields include id, name, labels, created_at, phase, and current_policy_version. Full spec/status are intentionally excluded — use sandbox get for detailed sandbox info.
  • Serialization uses serde_json::Value built via serde_json::json! macro (no new derive structs needed), with serde_yml::to_string for YAML output.

Components involved:

  • crates/openshell-cli/src/main.rs — enum, CLI arg definition, dispatch
  • crates/openshell-cli/src/run.rssandbox_list() output branching, sandbox_to_json() helper
  • crates/openshell-cli/Cargo.toml — add serde_yml workspace dependency
  • docs/sandboxes/manage-sandboxes.mdx — usage examples

Alternatives Considered

  1. Add only JSON, not YAML. Simpler but inconsistent with the provider profile commands that support both. Adding YAML costs very little once the serde_json::Value intermediate is built.

  2. Serialize the full protobuf Sandbox struct. Would expose spec (environment variables, image, policy) and status (conditions, pod info) in the list output. Rejected because the protobuf structs don't derive serde::Serialize, would require deep DTO conversion, and sandbox get already serves the detailed-view use case.

  3. Create a separate SandboxListOutput enum instead of renaming ProviderProfileOutput. Would duplicate the same Table/Yaml/Json variants. Renaming to OutputFormat is cleaner and positions the enum for reuse by future commands.

Agent Investigation

  • Explored PR feat(providers): add custom profile registry #1170 to understand the existing -o json/-o yaml pattern: ProviderProfileOutput enum with clap::ValueEnum in crates/openshell-cli/src/main.rs (line 649), dispatch via as_str() to run.rs, serialization via serde_json::to_string_pretty / serde_yml::to_string in crates/openshell-providers/src/profiles.rs.
  • Inspected SandboxCommands::List definition (main.rs:1158-1180): takes limit, offset, ids, names, selector — no output format flag.
  • Traced the sandbox_list() implementation (run.rs:3048-3127): makes a gRPC list_sandboxes call, receives Vec<Sandbox>, renders a table with NAME/CREATED/PHASE columns using phase_name() and format_epoch_ms() helpers.
  • Confirmed the protobuf Sandbox struct (from proto/openshell.proto:233) does not derive serde::Serialize, ruling out direct serialization — a serde_json::Value intermediate is required.
  • Verified ProviderProfileOutput is only referenced within main.rs (9 occurrences), making it safe to rename to OutputFormat.
  • Confirmed serde_json is already a dependency of the CLI crate (Cargo.toml:25), serde_yml is available as a workspace dependency (Cargo.toml root line 71) but not yet wired into the CLI crate.
  • Checked ObjectId, ObjectName traits are already imported in run.rs:51, and ObjectMeta.labels is a HashMap<String, String> that serializes natively via serde_json::json!.

Checklist

  • I've reviewed existing issues and the architecture docs
  • This is a design proposal, not a "please build this" request

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:triage-neededOpened without agent diagnostics and needs triage

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions