Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ uv tool install -U openshell
### Create a sandbox

```bash
openshell sandbox create -- claude # or opencode, codex, ollama
openshell sandbox create -- claude # or opencode, codex, copilot, ollama
```

A gateway is created automatically on first use. To deploy on a remote host instead, pass `--remote user@host` to the create command.
Expand All @@ -45,7 +45,7 @@ The sandbox container includes the following tools by default:

| Category | Tools |
| ---------- | -------------------------------------------------------- |
| Agent | `claude`, `opencode`, `codex` |
| Agent | `claude`, `opencode`, `codex`, `copilot` |
| Language | `python` (3.13), `node` (22) |
| Developer | `gh`, `git`, `vim`, `nano` |
| Networking | `ping`, `dig`, `nslookup`, `nc`, `traceroute`, `netstat` |
Expand Down Expand Up @@ -115,7 +115,7 @@ Policies are declarative YAML files. Static sections (filesystem, process) are l

## Providers

Agents need credentials — API keys, tokens, service accounts. OpenShell manages these as **providers**: named credential bundles that are injected into sandboxes at creation. The CLI auto-discovers credentials for recognized agents (Claude, Codex, OpenCode) from your shell environment, or you can create providers explicitly with `openshell provider create`. Credentials never leak into the sandbox filesystem; they are injected as environment variables at runtime.
Agents need credentials — API keys, tokens, service accounts. OpenShell manages these as **providers**: named credential bundles that are injected into sandboxes at creation. The CLI auto-discovers credentials for recognized agents (Claude, Codex, OpenCode, Copilot) from your shell environment, or you can create providers explicitly with `openshell provider create`. Credentials never leak into the sandbox filesystem; they are injected as environment variables at runtime.

## GPU Support

Expand All @@ -136,6 +136,7 @@ The CLI auto-bootstraps a GPU-enabled gateway on first use. GPU intent is also i
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | [`base`](https://github.com/NVIDIA/OpenShell-Community/tree/main/sandboxes/base) | Works out of the box. Provider uses `ANTHROPIC_API_KEY`. |
| [OpenCode](https://opencode.ai/) | [`base`](https://github.com/NVIDIA/OpenShell-Community/tree/main/sandboxes/base) | Works out of the box. Provider uses `OPENAI_API_KEY` or `OPENROUTER_API_KEY`. |
| [Codex](https://developers.openai.com/codex) | [`base`](https://github.com/NVIDIA/OpenShell-Community/tree/main/sandboxes/base) | Works out of the box. Provider uses `OPENAI_API_KEY`. |
| [GitHub Copilot CLI](https://docs.github.com/en/copilot/github-copilot-in-the-cli) | [`base`](https://github.com/NVIDIA/OpenShell-Community/tree/main/sandboxes/base) | Works out of the box. Provider uses `GITHUB_TOKEN` or `COPILOT_GITHUB_TOKEN`. |
| [OpenClaw](https://openclaw.ai/) | [Community](https://github.com/NVIDIA/OpenShell-Community) | Launch with `openshell sandbox create --from openclaw`. |
| [Ollama](https://ollama.com/) | [Community](https://github.com/NVIDIA/OpenShell-Community) | Launch with `openshell sandbox create --from ollama`. |

Expand Down
19 changes: 19 additions & 0 deletions crates/openshell-providers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ impl ProviderRegistry {
let mut registry = Self::default();
registry.register(providers::claude::ClaudeProvider);
registry.register(providers::codex::CodexProvider);
registry.register(providers::copilot::CopilotProvider);
registry.register(providers::opencode::OpencodeProvider);
registry.register(providers::generic::GenericProvider);
registry.register(providers::openai::OpenaiProvider);
Expand Down Expand Up @@ -128,6 +129,7 @@ pub fn normalize_provider_type(input: &str) -> Option<&'static str> {
match normalized.as_str() {
"claude" => Some("claude"),
"codex" => Some("codex"),
"copilot" | "gh-copilot" | "github-copilot" => Some("copilot"),
Comment thread
htekdev marked this conversation as resolved.
Outdated
"opencode" => Some("opencode"),
"generic" => Some("generic"),
"openai" => Some("openai"),
Expand Down Expand Up @@ -164,6 +166,9 @@ mod tests {
assert_eq!(normalize_provider_type("openai"), Some("openai"));
assert_eq!(normalize_provider_type("anthropic"), Some("anthropic"));
assert_eq!(normalize_provider_type("nvidia"), Some("nvidia"));
assert_eq!(normalize_provider_type("copilot"), Some("copilot"));
assert_eq!(normalize_provider_type("gh-copilot"), Some("copilot"));
assert_eq!(normalize_provider_type("github-copilot"), Some("copilot"));
assert_eq!(normalize_provider_type("unknown"), None);
}

Expand All @@ -181,5 +186,19 @@ mod tests {
detect_provider_from_command(&["/usr/bin/bash".to_string()]),
None
);
// Copilot standalone binary
assert_eq!(
detect_provider_from_command(&["copilot".to_string()]),
Some("copilot")
);
assert_eq!(
detect_provider_from_command(&["/usr/local/bin/copilot".to_string()]),
Some("copilot")
);
// gh alone still maps to github
assert_eq!(
detect_provider_from_command(&["gh".to_string()]),
Some("github")
);
}
}
46 changes: 46 additions & 0 deletions crates/openshell-providers/src/providers/copilot.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::{
ProviderDiscoverySpec, ProviderError, ProviderPlugin, RealDiscoveryContext, discover_with_spec,
};

pub struct CopilotProvider;

pub const SPEC: ProviderDiscoverySpec = ProviderDiscoverySpec {
id: "copilot",
credential_env_vars: &["COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN"],
};

impl ProviderPlugin for CopilotProvider {
fn id(&self) -> &'static str {
SPEC.id
}

fn discover_existing(&self) -> Result<Option<crate::DiscoveredProvider>, ProviderError> {
discover_with_spec(&SPEC, &RealDiscoveryContext)
}

fn credential_env_vars(&self) -> &'static [&'static str] {
SPEC.credential_env_vars
}
}

#[cfg(test)]
mod tests {
use super::SPEC;
use crate::discover_with_spec;
use crate::test_helpers::MockDiscoveryContext;

#[test]
fn discovers_copilot_env_credentials() {
let ctx = MockDiscoveryContext::new().with_env("COPILOT_GITHUB_TOKEN", "ghp-copilot-token");
let discovered = discover_with_spec(&SPEC, &ctx)
.expect("discovery")
.expect("provider");
assert_eq!(
discovered.credentials.get("COPILOT_GITHUB_TOKEN"),
Some(&"ghp-copilot-token".to_string())
);
}
}
1 change: 1 addition & 0 deletions crates/openshell-providers/src/providers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
pub mod anthropic;
pub mod claude;
pub mod codex;
pub mod copilot;
pub mod generic;
pub mod github;
pub mod gitlab;
Expand Down
16 changes: 16 additions & 0 deletions crates/openshell-sandbox/testdata/sandbox-policy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,22 @@ network_policies:
binaries:
- { path: /usr/bin/git }

copilot:
name: copilot
endpoints:
- { host: github.com, port: 443 }
- { host: api.github.com, port: 443 }
- { host: api.githubcopilot.com, port: 443 }
- { host: api.enterprise.githubcopilot.com, port: 443 }
- { host: release-assets.githubusercontent.com, port: 443 }
binaries:
- { path: "/usr/lib/node_modules/@github/copilot/node_modules/@github/**/copilot" }
- { path: /usr/local/bin/copilot }
- { path: "/home/*/.local/bin/copilot" }
- { path: /usr/bin/node }
- { path: /usr/bin/gh }
- { path: /usr/local/bin/gh }

gitlab:
name: gitlab
endpoints:
Expand Down
1 change: 1 addition & 0 deletions docs/about/supported-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The following table summarizes the agents that run in OpenShell sandboxes. All a
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | [`base`](https://github.com/NVIDIA/OpenShell-Community/tree/main/sandboxes/base) | Full coverage | Works out of the box. Requires `ANTHROPIC_API_KEY`. |
| [OpenCode](https://opencode.ai/) | [`base`](https://github.com/NVIDIA/OpenShell-Community/tree/main/sandboxes/base) | Partial coverage | Pre-installed. Add `opencode.ai` endpoint and OpenCode binary paths to the policy for full functionality. |
| [Codex](https://developers.openai.com/codex) | [`base`](https://github.com/NVIDIA/OpenShell-Community/tree/main/sandboxes/base) | No coverage | Pre-installed. Requires a custom policy with OpenAI endpoints and Codex binary paths. Requires `OPENAI_API_KEY`. |
| [GitHub Copilot CLI](https://docs.github.com/en/copilot/github-copilot-in-the-cli) | [`base`](https://github.com/NVIDIA/OpenShell-Community/tree/main/sandboxes/base) | Full coverage | Pre-installed. Works out of the box. Requires `GITHUB_TOKEN` or `COPILOT_GITHUB_TOKEN`. |
| [OpenClaw](https://openclaw.ai/) | [`openclaw`](https://github.com/NVIDIA/OpenShell-Community/tree/main/sandboxes/openclaw) | Bundled | Agent orchestration layer. Launch with `openshell sandbox create --from openclaw`. |
| [Ollama](https://ollama.com/) | [`ollama`](https://github.com/NVIDIA/OpenShell-Community/tree/main/sandboxes/ollama) | Bundled | Run cloud and local models. Includes Claude Code, Codex, and OpenClaw. Launch with `openshell sandbox create --from ollama`. |

Expand Down
Loading