NVIDIA / NVIDIA/NemoClaw

Isolate the OpenShell integration and migrate eligible CLI operations to the SDK or gRPC

Open
#9,802 0 comments 0 reactions 2 assignees Claimed by @cv View on GitHub
area: architecture area: cli integration: openclaw
Dominant language
TypeScript
Stars
22.5k
Forks
3.1k
Avg merge
1d 1h
Merged PRs (30d)
715

Description

# Summary

NemoClaw currently depends on 44 distinct OpenShell operation contracts: 43 CLI command shapes plus `openshell --version`. CLI command construction, environment handling, output parsing, and error-text classification reach into actions, onboarding, state detection, and recovery.

This Epic will:

1. Establish a typed, asynchronous NemoClaw-owned interface for OpenShell capabilities.
2. Move production CLI usage behind a CLI implementation of that interface.
3. Add SDK and direct gRPC implementations alongside it.
4. Migrate each capability to the highest-priority transport that proves semantic parity.
5. Retain the CLI implementation only where no stable RPC exists or the behavior is host-side.

This is a refactor and transport migration. It must not intentionally change supported NemoClaw behavior.

## Current position

NemoClaw currently pins OpenShell `v0.0.106` in `nemoclaw-blueprint/blueprint.yaml`. The TypeScript SDK is present in OpenShell `v0.0.109` and `v0.0.110`, so adopting it also requires a separately reviewed OpenShell dependency upgrade.

This coverage snapshot uses [OpenShell v0.0.110](https://github.com/NVIDIA/OpenShell/releases/tag/v0.0.110), verified on 2026-08-20. The [TypeScript SDK README](https://github.com/NVIDIA/OpenShell/blob/v0.0.110/sdk/typescript/README.md) recommends using the SDK and gateway from the same release.

The SDK is currently distributed through GitHub Packages and requires `read:packages`. Public npm distribution is planned but is not yet the documented installation path.

## OpenShell 0.1.0 relationship

OpenShell 0.1.0 is the parity target for public sandbox operations because OpenShell `main` already exposes the create, observe, start, stop, delete, exec, policy, provider, GPU, SSH, forwarding, and status capabilities used by this Epic. Phase 1 may continue against current behavior. Phase 2 may implement and test released main-service and SDK contracts before 0.1.0, but the production selector remains pinned until the separately reviewed dependency migration passes.

This Epic owns how NemoClaw calls public OpenShell capabilities. Decisions caused by the 0.1.0 trust boundary and runtime topology remain outside this Epic and require separate acceptance before implementation. These include direct runtime-engine mutations, protected application configuration, fine-grained process recovery, legacy sandbox migration, and release-bundle qualification.

The typed OpenShell interface must not expose generic privileged exec, run-as, or direct Docker, Podman, Kubernetes, or VM mutation for an OpenShell-owned sandbox. A capability with no public OpenShell contract remains on its current path until an accepted product decision records its disposition. This Epic does not treat that path as a 0.1.0 compatibility mechanism.

## Problem

### CLI transport concerns leak into business logic

NemoClaw consumers frequently know:

- Exact argument ordering and gateway flags.
- Which environment variables must be present.
- Whether output appears on stdout or stderr.
- How OpenShell tables and YAML are formatted.
- Which English error messages represent missing resources, authentication failures, transport failures, or recoverable gateway state.
- Which exit codes and process signals imply a particular domain result.

Examples include:

- `src/lib/state/gateway.ts`, which parses changing `sandbox list` columns and contains version-specific error-text compatibility handling.
- `src/lib/actions/sandbox/gateway-state.ts`, which classifies gateway failures using regular expressions over CLI output.
- `src/lib/onboard/providers.ts`, which constructs provider flags and works around the CLI rejecting `--credential KEY` when the local environment variable is empty.
- `src/lib/adapters/openshell/runtime.ts`, whose mostly synchronous process interface returns process-shaped results and can call `process.exit()`.

This makes action behavior harder to test independently and makes CLI formatting changes capable of affecting state classification or recovery.

### The SDK does not yet cover every NemoClaw use case

The SDK has strong curated support for sandbox operations, but gateway-scoped providers, inference routing, persistent forwards, file transfer, and parts of gateway management are incomplete or intentionally outside its scope.

The generated `client.raw` API can reach the RPCs on the main [OpenShell service](https://github.com/NVIDIA/OpenShell/blob/v0.0.110/proto/openshell.proto). The separate [inference service](https://github.com/NVIDIA/OpenShell/blob/v0.0.110/proto/inference.proto) exposes route operations, but the TypeScript SDK does not generate or export that client.

Direct gRPC can replace more CLI usage, but it has a higher ownership cost than a curated SDK method. NemoClaw must pin the protobuf contract, contain generated and wire types, and test status, cancellation, streaming, authentication, and schema compatibility.

### CLI problems the SDK can address

For migrated operations, the SDK should reduce:

- Parsing of human-oriented tables, YAML, and error text.
- Child-process startup and stdout, stderr, timeout, signal, and buffer handling.
- Dependence on ambient gateway selection.
- Synchronous blocking in Node.js.
- Process-shaped errors flowing into action decisions.
- Test setup based on mocked subprocess output.

The SDK will not fix gateway defects, authentication failures, or incomplete server semantics. It also shifts some responsibility into NemoClaw: the current TypeScript SDK accepts explicit gateway and TLS material, uses static authentication for the client lifetime, and does not currently export automatic OIDC refresh. See the [SDK transport contract](https://github.com/NVIDIA/OpenShell/blob/v0.0.110/sdk/typescript/src/transport.ts).

## Target architecture

```mermaid
flowchart LR
C["Actions, onboarding, state, and recovery"] --> I["Typed NemoClaw OpenShell interface"]
I --> SDK["Curated SDK implementation
first choice"]
I --> RPC["Direct gRPC implementation
second choice"]
I --> CLI["CLI implementation
remaining capabilities"]
SDK --> G["OpenShell gateway"]
RPC --> G
CLI --> B["openshell executable"]
B --> G
```

The interface should be shaped around NemoClaw capabilities, not around transport APIs. Consumers should receive typed requests, results, and errors without seeing argv, stdout, stderr, or protobuf wire types.

## Functional coverage

The earlier 12-of-44 estimate counted apparent method mappings. The matrix below applies a stricter standard to the curated SDK and direct gRPC contracts in OpenShell `v0.0.110`.

```mermaid
flowchart LR
SDK["Curated SDK
sandbox reads, core lifecycle,
exec, SSH, attachments, configuration"]
RPC["Direct gRPC
gateway configuration, providers,
logs, policy status, inference routes"]
CLI["CLI or host-side implementation
local gateway lifecycle, file transfer,
detached forwards, terminal and build glue"]
SDK -->|"when no curated method exists"| RPC
RPC -->|"when no stable RPC exists"| CLI
```

| Functional slice | What's covered in the SDK | Available through gRPC? | What remains on the CLI after SDK and gRPC migration |
|---|---|---|---|
| Gateway targeting and lifecycle | Health checks against a known gateway. | Partial. The main service provides health, gateway information, and gateway configuration. Local gateway registry and process operations are not RPCs. | Gateway discovery, registration, selection, removal, destruction, local process lifecycle, and recovery. |
| Sandbox inventory and readiness | Typed `get`, `list`, `waitReady`, and `waitDeleted`. | Yes. The main service exposes sandbox reads and observed state. | Gateway ownership and selection remain until the gateway lifecycle slice migrates. |
| Sandbox create and delete | Basic `create` and `delete`, including images, labels, environment, providers, GPU, policy, and advanced fields through `rawSpec`. | Partial. Core create, delete, start, and stop operations are RPCs. Local build, upload, and detached-forward composition are not. | Local Dockerfile builds, upload during creation, and detached-forward composition. |
| Exec, connect, and SSH | Buffered exec, streaming exec, interactive exec, and SSH-session creation and revocation. | Yes for exec streams and SSH-session lifecycle. Terminal mode, signal handling, and OpenSSH connection behavior remain host-side. | OpenShell CLI terminal and SSH connection glue unless NemoClaw rebuilds it on the SDK or gRPC primitives. |
| Providers and credentials | Attaching, listing, and detaching providers on a sandbox. | Partial. The main service provides provider create, get, list, update, delete, profiles, credential refresh, and sandbox attachments. Local credential discovery and request assembly are client-side. | Local credential ingestion and CLI-specific validation until NemoClaw owns equivalent client-side behavior. |
| Policy and configuration | Reading sandbox configuration, updating sandbox policy, waiting for policy application, and setting sandbox-scoped settings. | Partial. The main service provides sandbox and gateway configuration, policy status, and policy history. CLI file loading and merge semantics are client-side. | File-oriented policy handling and CLI-specific `--base` and `--full` composition until NemoClaw owns equivalent behavior. |
| Inference routing | No curated SDK support. | Yes, through the separate inference service. The TypeScript SDK does not generate this client, so NemoClaw would own the generated client. | The CLI remains until a release-pinned generated client passes the shared contract and live tests. |
| Port forwarding | Process-lifetime forwarding owned by the current Node.js process. | Partial. `ForwardTcp` is a live stream owned by the caller. It has no detached, cross-process lifecycle. | Detached background forward start, cross-process list, and stop behavior. |
| File transfer | No first-class file-transfer support. Exec with stdin can handle some payloads but is not equivalent. | No file-transfer RPC exists. | Sandbox upload, download, and copy workflows implemented through the CLI and SSH. |
| Logs, settings, diagnostics, and version | Gateway health and sandbox-scoped setting updates. | Partial. The main service provides sandbox logs and watch streams, gateway information, and gateway configuration. | Installed CLI version checks and local executable diagnostics. |

## Will SDK coverage expand?

The upstream SDK states a goal of agent parity and lists curated gateway and provider clients as planned. It does not provide a delivery date or guarantee that every CLI behavior will move into the SDK.

Some gaps are intentional:

- Terminal and OpenSSH user experience.
- Upload and download.
- Detached forwards that outlive the calling process.

This Epic must not depend on future upstream work. A capability remains on the CLI until a released SDK or gRPC contract passes NemoClaw's parity tests.

## Guiding principles

- Work in small, independently valuable functional slices.
- Characterize existing behavior before changing its transport.
- Prefer implementations in this order: **SDK > gRPC > CLI**.
- Use the curated SDK when it provides semantic parity.
- Use direct gRPC when a stable released RPC exists but the curated SDK has a gap.
- Keep generated clients and protobuf wire types inside the OpenShell implementation boundary.
- Keep the CLI when no stable RPC exists or the behavior is host-side.
- Keep business decisions independent of CLI, SDK, and gRPC details.
- Make the interface asynchronous from the beginning.
- Use typed requests, results, and errors.
- Keep gateway selection explicit.
- Prove parity with behavior tests, not line coverage alone.
- Do not use automatic cross-transport fallback for mutations. A timed-out request may already have succeeded, so a second transport could duplicate the mutation.
- Pin the SDK, protobuf contracts, generated clients, and gateway to one reviewed OpenShell release.
- Delete obsolete code only after the migrated slice has merged and produced the required evidence.
- Keep unsupported capabilities on a maintained CLI implementation. A mixed backend is an acceptable outcome.

## Phase 1: Establish the adapter layer

**Goal:** All in-scope production OpenShell CLI usage flows through a typed NemoClaw interface backed by one CLI implementation.

For each functional slice:

1. Add or strengthen action tests that capture current NemoClaw decisions.
2. Add the smallest typed interface needed by that slice.
3. Implement it using the current CLI behavior.
4. Migrate the slice's consumers.
5. Merge and validate the migrated slice.
6. Delete the now-unused direct command builders, parsers, or helpers.

Phase 1 should not create a second copy of the legacy implementation. Existing behavior moves into the CLI implementation and obsolete direct paths are deleted after their final consumer migrates.

"In scope" means production runtime calls from `src/` and `nemoclaw/src/`. Installer, build, diagnostics, E2E, and repository scripts that intentionally operate on the OpenShell executable should be inventoried and either approved as explicit exceptions or migrated separately. Treating every shell script as an application consumer would make Phase 1 much larger without improving the transport boundary.

### Phase 1 exit criteria

- A typed, asynchronous interface owns every in-scope OpenShell capability.
- Production consumers do not construct OpenShell argv or classify raw CLI output.
- One CLI implementation owns command construction, environment, parsing, timeout, and redaction.
- Action behavior remains unchanged.
- Direct legacy paths have been removed after their consumers migrate.
- Approved executable-level exceptions are documented.

## Phase 2: Replace CLI usage with SDK or direct gRPC

**Goal:** Replace CLI-backed operations with the curated SDK where possible and direct gRPC where needed. Keep CLI behavior that has no stable RPC or remains host-side.

For each eligible slice:

1. Confirm the exact target SDK, gateway, protobuf, and package identities.
2. Audit the current pin to target release range for behavior and packaging changes.
3. Implement the capability with the curated SDK when it provides semantic parity.
4. Otherwise, implement the capability with a release-pinned direct gRPC client.
5. Keep generated clients and protobuf wire types inside the implementation boundary.
6. Run the shared behavior contract against the CLI and replacement implementations.
7. Validate authentication, authorization, policy, lifecycle, and cleanup behavior at the real gateway boundary when required.
8. Select one transport explicitly for that capability.
9. Merge and observe the migrated path.
10. Delete the CLI method later only when no supported consumer still needs it.

A slice stays on the CLI when no stable RPC exists, lifecycle semantics differ, or runtime evidence is not adequate.

### Phase 2 exit criteria

- Every semantically equivalent capability uses the highest-priority viable transport: SDK, then direct gRPC, then CLI.
- Each selected implementation passes the shared contract tests while multiple implementations remain supported.
- Generated clients and protobuf wire types do not escape the OpenShell implementation boundary.
- Each CLI-retained capability has a specific reason and upstream dependency.
- No mutation uses an implicit cross-transport retry.
- Package installation works in contributor, CI, release, and installed-product environments.
- Authentication and token refresh ownership are defined and tested.
- Obsolete CLI methods have been removed after migration evidence.
- User-visible behavior and security boundaries remain unchanged.

## Testing strategy

| Test layer | What it protects |
|---|---|
| Action tests against a typed fake | NemoClaw decisions, state transitions, recovery, messages, and cleanup without knowledge of transport details. |
| CLI implementation tests | Exact argv, gateway targeting, environment, stdout and stderr parsing, timeout handling, error mapping, and credential redaction. |
| SDK and gRPC implementation tests | Request mapping, authentication metadata, status mapping, cancellation, streaming cleanup, and containment of wire types. |
| Shared contract tests | Behavior that every OpenShell implementation must provide. Introduce these when Phase 2 adds a second implementation. |
| Focused live tests | Authentication, policy application, provider credentials, lifecycle mutations, persistent forwarding, and cleanup behavior that cannot be proven locally. |

Coverage thresholds remain a useful ratchet, but they are not the acceptance criterion. Each slice needs tests for the decisions and failure modes it can change.

## Proposed slice order

Run the same capability order through both phases. During Phase 2, select the highest-priority viable transport for each capability:

1. Sandbox inventory and readiness.
2. Policy and sandbox configuration.
3. Sandbox create and delete.
4. Exec, interactive sessions, and SSH.
5. Providers and credentials.
6. Inference routing.
7. Forwards, file transfer, logs, and settings.
8. Gateway targeting, lifecycle, and diagnostics.
9. Plugin and approved script consumers.

Sandbox inventory is the recommended first slice because it is read-only, heavily affected by formatted-output parsing, and can establish the interface and testing pattern with low mutation risk.

## Risks and open decisions

- **Target version:** NemoClaw is pinned to `v0.0.106`; the SDK and gRPC migration requires choosing and auditing a newer exact release.
- **Package distribution:** GitHub Packages requires `read:packages`, and NemoClaw does not currently configure the `@nvidia` registry. Public npm availability may change the preferred rollout.
- **Authentication ownership:** SDK and direct gRPC clients require explicit gateway, TLS, and token material. Automatic gateway-registry loading and OIDC refresh currently belong to the CLI.
- **Direct gRPC ownership:** Main-service gaps can use `client.raw`. Inference routing requires a separately generated client. Maintainers must accept versioning, generation, and compatibility ownership.
- **Mixed transport lifetime:** Some CLI capabilities may remain indefinitely because OpenShell exposes no equivalent RPC or the behavior is host-side.
- **Scope boundary:** Confirm whether plugin runner and operational scripts belong in this Epic or in follow-up work.
- **Upstream roadmap:** Track gateway and provider client releases, but do not block safe CLI-adapter work on them.
- **Dependency migration:** The OpenShell release upgrade must be audited as a semantic migration, not treated as a package-version edit.

## Epic completion criteria

- Phase 1 and Phase 2 exit criteria are satisfied.
- All 44 operation contracts have a final disposition: SDK, direct gRPC, maintained CLI, or approved executable-level exception.
- The coverage matrix reflects the exact SDK and protobuf contracts selected by NemoClaw.
- Required tests and runtime evidence are linked from child issues.
- No unsupported SDK or gRPC behavior is documented as canonical NemoClaw behavior.
- Maintainers have accepted the resulting long-term SDK, gRPC, and CLI ownership boundary.

## Partial delivery acceptance

A capability can use more than one PR. A partial PR is acceptable only when:

- The PR names each in-scope production consumer that it does not migrate.
- Each deferred consumer is explicitly accepted as out of scope and tracked by a linked follow-on issue with an assigned implementation owner before the partial PR merges.
- The capability issue may close once its own acceptance criteria are met.
- The follow-on issue owns the deferred implementation and links the originating capability issue and partial PR.
- Phase 1 closure issue #9813 records each final consumer disposition.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.