Skip to content

Commit eff88b7

Browse files
authored
feat(providers): add GitHub Copilot CLI agent provider (#476)
* feat(providers): add GitHub Copilot CLI agent provider Source: https://docs.github.com/en/copilot/reference/copilot-allowlist-reference
1 parent 495fe4c commit eff88b7

File tree

6 files changed

+89
-3
lines changed

6 files changed

+89
-3
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ uv tool install -U openshell
3636
### Create a sandbox
3737

3838
```bash
39-
openshell sandbox create -- claude # or opencode, codex, ollama
39+
openshell sandbox create -- claude # or opencode, codex, copilot, ollama
4040
```
4141

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

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

116116
## Providers
117117

118-
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.
118+
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.
119119

120120
## GPU Support
121121

@@ -136,6 +136,7 @@ The CLI auto-bootstraps a GPU-enabled gateway on first use. GPU intent is also i
136136
| [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`. |
137137
| [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`. |
138138
| [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`. |
139+
| [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`. |
139140
| [OpenClaw](https://openclaw.ai/) | [Community](https://github.com/NVIDIA/OpenShell-Community) | Launch with `openshell sandbox create --from openclaw`. |
140141
| [Ollama](https://ollama.com/) | [Community](https://github.com/NVIDIA/OpenShell-Community) | Launch with `openshell sandbox create --from ollama`. |
141142

crates/openshell-providers/src/lib.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ impl ProviderRegistry {
7777
let mut registry = Self::default();
7878
registry.register(providers::claude::ClaudeProvider);
7979
registry.register(providers::codex::CodexProvider);
80+
registry.register(providers::copilot::CopilotProvider);
8081
registry.register(providers::opencode::OpencodeProvider);
8182
registry.register(providers::generic::GenericProvider);
8283
registry.register(providers::openai::OpenaiProvider);
@@ -128,6 +129,7 @@ pub fn normalize_provider_type(input: &str) -> Option<&'static str> {
128129
match normalized.as_str() {
129130
"claude" => Some("claude"),
130131
"codex" => Some("codex"),
132+
"copilot" => Some("copilot"),
131133
"opencode" => Some("opencode"),
132134
"generic" => Some("generic"),
133135
"openai" => Some("openai"),
@@ -164,6 +166,7 @@ mod tests {
164166
assert_eq!(normalize_provider_type("openai"), Some("openai"));
165167
assert_eq!(normalize_provider_type("anthropic"), Some("anthropic"));
166168
assert_eq!(normalize_provider_type("nvidia"), Some("nvidia"));
169+
assert_eq!(normalize_provider_type("copilot"), Some("copilot"));
167170
assert_eq!(normalize_provider_type("unknown"), None);
168171
}
169172

@@ -181,5 +184,19 @@ mod tests {
181184
detect_provider_from_command(&["/usr/bin/bash".to_string()]),
182185
None
183186
);
187+
// Copilot standalone binary
188+
assert_eq!(
189+
detect_provider_from_command(&["copilot".to_string()]),
190+
Some("copilot")
191+
);
192+
assert_eq!(
193+
detect_provider_from_command(&["/usr/local/bin/copilot".to_string()]),
194+
Some("copilot")
195+
);
196+
// gh alone still maps to github
197+
assert_eq!(
198+
detect_provider_from_command(&["gh".to_string()]),
199+
Some("github")
200+
);
184201
}
185202
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use crate::{
5+
ProviderDiscoverySpec, ProviderError, ProviderPlugin, RealDiscoveryContext, discover_with_spec,
6+
};
7+
8+
pub struct CopilotProvider;
9+
10+
pub const SPEC: ProviderDiscoverySpec = ProviderDiscoverySpec {
11+
id: "copilot",
12+
credential_env_vars: &["COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN"],
13+
};
14+
15+
impl ProviderPlugin for CopilotProvider {
16+
fn id(&self) -> &'static str {
17+
SPEC.id
18+
}
19+
20+
fn discover_existing(&self) -> Result<Option<crate::DiscoveredProvider>, ProviderError> {
21+
discover_with_spec(&SPEC, &RealDiscoveryContext)
22+
}
23+
24+
fn credential_env_vars(&self) -> &'static [&'static str] {
25+
SPEC.credential_env_vars
26+
}
27+
}
28+
29+
#[cfg(test)]
30+
mod tests {
31+
use super::SPEC;
32+
use crate::discover_with_spec;
33+
use crate::test_helpers::MockDiscoveryContext;
34+
35+
#[test]
36+
fn discovers_copilot_env_credentials() {
37+
let ctx = MockDiscoveryContext::new().with_env("COPILOT_GITHUB_TOKEN", "ghp-copilot-token");
38+
let discovered = discover_with_spec(&SPEC, &ctx)
39+
.expect("discovery")
40+
.expect("provider");
41+
assert_eq!(
42+
discovered.credentials.get("COPILOT_GITHUB_TOKEN"),
43+
Some(&"ghp-copilot-token".to_string())
44+
);
45+
}
46+
}

crates/openshell-providers/src/providers/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
pub mod anthropic;
55
pub mod claude;
66
pub mod codex;
7+
pub mod copilot;
78
pub mod generic;
89
pub mod github;
910
pub mod gitlab;

crates/openshell-sandbox/testdata/sandbox-policy.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,26 @@ network_policies:
5757
binaries:
5858
- { path: /usr/bin/git }
5959

60+
copilot:
61+
name: copilot
62+
endpoints:
63+
- { host: github.com, port: 443 }
64+
- { host: api.github.com, port: 443 }
65+
- { host: api.githubcopilot.com, port: 443 }
66+
- { host: api.individual.githubcopilot.com, port: 443 }
67+
- { host: api.business.githubcopilot.com, port: 443 }
68+
- { host: api.enterprise.githubcopilot.com, port: 443 }
69+
- { host: copilot-proxy.githubusercontent.com, port: 443 }
70+
- { host: copilot-telemetry.githubusercontent.com, port: 443 }
71+
- { host: default.exp-tas.com, port: 443 }
72+
- { host: origin-tracker.githubusercontent.com, port: 443 }
73+
- { host: release-assets.githubusercontent.com, port: 443 }
74+
binaries:
75+
- { path: "/usr/lib/node_modules/@github/copilot/node_modules/@github/**/copilot" }
76+
- { path: /usr/local/bin/copilot }
77+
- { path: "/home/*/.local/bin/copilot" }
78+
- { path: /usr/bin/node }
79+
6080
gitlab:
6181
name: gitlab
6282
endpoints:

docs/about/supported-agents.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ The following table summarizes the agents that run in OpenShell sandboxes. All a
77
| [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`. |
88
| [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. |
99
| [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`. |
10+
| [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`. |
1011
| [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`. |
1112
| [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`. |
1213

0 commit comments

Comments
 (0)