Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/branch-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,32 @@ jobs:

- name: Lint
run: mise run markdown:lint

sdk-typescript:
name: TypeScript SDK
needs: pr_metadata
if: needs.pr_metadata.outputs.should_run == 'true'
runs-on: linux-amd64-cpu8
container:
image: ghcr.io/nvidia/openshell/ci:latest
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- name: Install tools
run: mise install --locked

- name: Check TypeScript SDK
run: mise run sdk:ts:ci

# Exercise the full release publish path (version stamp, dist-tag,
# prepublishOnly, tarball) without uploading. Uses the off-tag dev
# version, which validates the prerelease dist-tag branch too.
- name: Verify publishable artifact (dry-run)
env:
OPENSHELL_NPM_PUBLISH_ARGS: --dry-run
run: |
OPENSHELL_NPM_VERSION="$(uv run python tasks/scripts/release.py get-version --npm)" \
mise run sdk:ts:publish
40 changes: 40 additions & 0 deletions .github/workflows/release-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ jobs:
outputs:
python_version: ${{ steps.v.outputs.python }}
cargo_version: ${{ steps.v.outputs.cargo }}
npm_version: ${{ steps.v.outputs.npm }}
deb_version: ${{ steps.v.outputs.deb }}
rpm_version: ${{ steps.v.outputs.rpm_version }}
rpm_release: ${{ steps.v.outputs.rpm_release }}
Expand All @@ -65,6 +66,7 @@ jobs:
set -euo pipefail
echo "python=$(uv run python tasks/scripts/release.py get-version --python)" >> "$GITHUB_OUTPUT"
echo "cargo=$(uv run python tasks/scripts/release.py get-version --cargo)" >> "$GITHUB_OUTPUT"
echo "npm=$(uv run python tasks/scripts/release.py get-version --npm)" >> "$GITHUB_OUTPUT"
echo "deb=$(uv run python tasks/scripts/release.py get-version --deb)" >> "$GITHUB_OUTPUT"
echo "rpm_version=$(uv run python tasks/scripts/release.py get-version --rpm-version)" >> "$GITHUB_OUTPUT"
echo "rpm_release=$(uv run python tasks/scripts/release.py get-version --rpm-release)" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -1067,6 +1069,44 @@ jobs:
working-directory: ./fern
run: fern generate --docs

publish-sdk-typescript:
name: Publish TypeScript SDK
needs: [compute-versions, release]
runs-on: linux-amd64-cpu8
timeout-minutes: 15
permissions:
contents: read
packages: write
container:
image: ghcr.io/nvidia/openshell/ci:latest
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ inputs.tag || github.ref }}

- name: Mark workspace safe for git
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Install tools
run: mise install --locked

- name: Configure npm auth for GitHub Packages
working-directory: ./sdk/typescript
run: |
{
echo "@nvidia:registry=https://npm.pkg.github.com"
echo '//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}'
} > .npmrc

- name: Publish
env:
OPENSHELL_NPM_VERSION: ${{ needs.compute-versions.outputs.npm_version }}
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: mise run sdk:ts:publish

release-helm:
name: Release Helm Chart (OCI)
needs: [compute-versions, release, tag-ghcr-release]
Expand Down
30 changes: 30 additions & 0 deletions buf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Repo-level buf module. Declares proto/ as the single module so buf generate,
# buf lint, buf breaking, and the editor LSP all resolve imports the same way.
# Code generation lives with each consumer (see sdk/typescript/buf.gen.yaml);
# this file owns the module boundary and proto validation policy.
version: v2
modules:
- path: proto
lint:
use:
- STANDARD
except:
# Flat proto/ layout: all files live in one directory with nested
# packages (openshell.v1, openshell.sandbox.v1, ...). Adopting these
# would require restructuring the tree into openshell/<svc>/v1/ and
# updating every Rust/Python/TS codegen path and import.
- DIRECTORY_SAME_PACKAGE
- PACKAGE_DIRECTORY_MATCH
# Established API shape: services are unsuffixed (OpenShell, not
# OpenShellService) and RPCs reuse shared request/response messages with
# short names. Renaming these is a breaking change across the codebase.
- RPC_REQUEST_RESPONSE_UNIQUE
- RPC_REQUEST_STANDARD_NAME
- RPC_RESPONSE_STANDARD_NAME
- SERVICE_SUFFIX
breaking:
use:
- FILE
3 changes: 3 additions & 0 deletions docs/_components/jsx.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

declare const React: unknown;
3 changes: 3 additions & 0 deletions fern/components/CustomFooter.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

/**
* Custom footer for NVIDIA docs (Fern native header/footer).
* Markup and class names match the original custom-app footer 1:1 so that
Expand Down
9 changes: 9 additions & 0 deletions scripts/update_license_headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
".yaml": "#",
".yml": "#",
".rego": "#",
".ts": "//",
".tsx": "//",
".mts": "//",
".cts": "//",
}

# Directories to skip entirely (relative to repo root).
Expand All @@ -55,6 +59,7 @@
".git",
".cache",
"python/openshell/_proto",
"sdk/typescript/src/gen",
}

# Individual filenames to skip.
Expand Down Expand Up @@ -103,6 +108,10 @@ def is_excluded(rel: Path) -> bool:
"""Return True if a path should be skipped."""
rel_str = str(rel)

# Vendored dependencies never carry our headers, at any depth.
if "node_modules" in rel.parts:
return True

# Exact filename exclusions.
if rel.name in EXCLUDE_FILES:
return True
Expand Down
5 changes: 5 additions & 0 deletions sdk/typescript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules/
src/gen/
dist/
*.tsbuildinfo
.npmrc
62 changes: 62 additions & 0 deletions sdk/typescript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# @nvidia/openshell-sdk

TypeScript client for the OpenShell gateway — thin, idiomatic bindings generated from the OpenShell protobufs.

Distributed via GitHub Packages during pre-GA. Public npm (`@openshell/sdk`) follows at GA; the public API is unchanged across the move, so only the install specifier changes.

## Install

Published to GitHub Packages, so add a project `.npmrc`:

```shell
@nvidia:registry=https://npm.pkg.github.com
```

Authenticate with a GitHub token that has `read:packages`, then:

```shell
npm install @nvidia/openshell-sdk
```

## Usage

```ts
import { OpenShellClient } from '@nvidia/openshell-sdk'

const client = await OpenShellClient.connect({
gateway: 'https://gateway.example.com',
oidcToken: process.env.OPENSHELL_TOKEN,
})

const sandbox = await client.sandbox.create({
image: 'ghcr.io/nvidia/openshell-community/sandboxes/python:latest',
})
await client.sandbox.waitReady(sandbox.name, 120)

const result = await client.sandbox.exec(sandbox.name, ['/bin/sh', '-c', 'echo hello'])
console.log(result.stdout.toString())

await client.sandbox.delete(sandbox.name)
```

### Scoped clients

`client.sandbox` is a `SandboxClient`. If you only need sandboxes, connect one
directly — same API, one less hop:

```ts
import { SandboxClient } from '@nvidia/openshell-sdk'

const sandbox = await SandboxClient.connect({ gateway, oidcToken })
await sandbox.create({ image })
```

## Development

The version field is a `0.0.0` placeholder; CI stamps the real version from the git release tag at publish time, matching the Rust and Python packages.

```shell
mise run sdk:ts:proto # generate stubs from proto/ (protoc + protoc-gen-es)
mise run sdk:ts:typecheck # tsc --noEmit
mise run sdk:ts:build # emit dist/
```
24 changes: 24 additions & 0 deletions sdk/typescript/buf.gen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Code generation for the TypeScript SDK. The proto module boundary and
# validation policy live in the repo-level buf.yaml; this template only drives
# generation. buf compiles the module with its own compiler (no protoc) and
# runs the connect-es plugin from this package's devDependencies. Limited to
# the client-surface closure so we don't emit the unused inference/compute/test
# protos; well-known types resolve through @bufbuild/protobuf/wkt and are not
# generated.
version: v2
clean: true
inputs:
- directory: ../../proto
paths:
- ../../proto/openshell.proto
- ../../proto/sandbox.proto
- ../../proto/datamodel.proto
plugins:
- local: ./node_modules/.bin/protoc-gen-es
out: src/gen
opt:
- target=ts
- import_extension=js
Loading
Loading