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
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,8 @@ COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod a+rx /usr/local/bin/entrypoint.sh \
/usr/local/share/enclave/auth-reconcile.sh \
/usr/local/share/enclave/net.sh && \
chmod a+r /usr/local/share/enclave/tmux-session.conf
chmod a+r /usr/local/share/enclave/tmux-session.conf \
/usr/local/share/enclave/kit-init.sh
USER ${USERNAME}
Comment thread
xai marked this conversation as resolved.
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["/bin/bash"]
4 changes: 4 additions & 0 deletions docs/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ are denied.

Codex OAuth note: the OAuth callback redirects to `http://localhost:1455/auth/callback`. enclave auto-maps port 1455 when no session exists. If you need to re-login, add `-p 1455`. Gateway logs will show `Loopback proxy (socat) enabled on port 1455` when forwarding is active.

Pi Anthropic OAuth note: Enclave does not auto-map the callback port. Add
`-p 53692` when starting a Pi session in which you intend to log in with
Anthropic OAuth.

OpenCode ChatGPT subscription note: for `opencode`, complete OpenAI browser auth from the CLI inside the container rather than from `/connect` in the TUI. Use:

```bash
Expand Down
22 changes: 16 additions & 6 deletions extensions/tools/pi/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Pi

Pi is a coding agent by Mario Zechner (`@earendil-works/pi-coding-agent`). It
supports multiple LLM providers (OpenAI, Anthropic, Google).
supports multiple LLM providers (OpenAI, Anthropic, Google, Mistral).

## Configuration

Expand All @@ -10,26 +10,36 @@ supports multiple LLM providers (OpenAI, Anthropic, Google).
- **Config directory**: `~/.pi`
- **Settings file**: `~/.pi/agent/settings.json`

## Environment

Enclave sets `PI_TELEMETRY=0` and `PI_SKIP_VERSION_CHECK=1` to disable Pi's
install/update telemetry and version check while leaving model catalog refresh
enabled. See Pi's upstream [environment-variable reference](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/environment-variables.md).

## API Keys

| Variable | Purpose |
|----------|---------|
| `OPENAI_API_KEY` | OpenAI API access |
| `ANTHROPIC_API_KEY` | Anthropic API access |
| `GEMINI_API_KEY` | Google Gemini API access |
| `MISTRAL_API_KEY` | Mistral API access |

## Auth Files

- `agent/auth.json`

Pi uses Codex OAuth for OpenAI subscriptions. enclave auto-maps port 1455
when the `openai-codex` entry is missing from `agent/auth.json`; add `-p 1455`
(or `-p 1455:1455`) to re-login.
Enclave recognizes Pi's `openai`, `openai-codex`, `anthropic`, `google`, and
`mistral` credentials in this file. For browser-based login, Enclave auto-maps
port 1455 when the `openai-codex` entry is missing. Anthropic OAuth is opt-in;
add `-p 53692` when logging in. Add `-p 1455` to re-login with Codex when an
Comment thread
xai marked this conversation as resolved.
entry already exists.

## Network Access

Allowlisted domains include OpenAI, Anthropic, Google, GitHub, and common
package registries (npm, PyPI, Go, CDNs, TLS/OCSP).
Allowlisted domains include `pi.dev` for Pi's model catalog, OpenAI, Anthropic,
Google, Mistral, GitHub, and common package registries (npm, PyPI, Go, CDNs,
TLS/OCSP).

## Settings

Expand Down
7 changes: 7 additions & 0 deletions extensions/tools/pi/gateway-allowlist.conf
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ no-resolv
no-poll
address=/#/

# Pi model catalogs
server=/pi.dev/8.8.8.8

# OpenAI
conf-file=/etc/dnsmasq.allowlists/fragments/openai.conf

Expand All @@ -19,6 +22,10 @@ conf-file=/etc/dnsmasq.allowlists/fragments/anthropic.conf
# Google / Gemini
conf-file=/etc/dnsmasq.allowlists/fragments/google.conf

# Pi uses Mistral's general API; the shared fragment also allows Codestral,
# which uses a separate credential.
server=/api.mistral.ai/8.8.8.8
Comment thread
xai marked this conversation as resolved.

# Common Infrastructure
conf-file=/etc/dnsmasq.allowlists/fragments/github.conf
conf-file=/etc/dnsmasq.allowlists/fragments/npm.conf
Expand Down
38 changes: 33 additions & 5 deletions extensions/tools/pi/spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
schemaVersion: "1"
kind: sandbox
name: pi
displayName: Anthropic Claude Pi
description: Anthropic Claude Pi
displayName: Pi Coding Agent
description: A minimal terminal coding harness

sandbox:
entrypoint: { run: [pi] }
Expand All @@ -31,11 +31,17 @@ sandbox:
- agent/skills/
- agent/themes/

environment:
variables:
PI_SKIP_VERSION_CHECK: "1"
PI_TELEMETRY: "0"
Comment thread
xai marked this conversation as resolved.

credentials:
sources:
openai-api-key: { env: [OPENAI_API_KEY] }
anthropic-api-key: { env: [ANTHROPIC_API_KEY] }
gemini-api-key: { env: [GEMINI_API_KEY] }
mistral-api-key: { env: [MISTRAL_API_KEY] }

network:
serviceDomains:
Expand All @@ -44,21 +50,43 @@ network:
api.anthropic.com: anthropic-api-key
"*.anthropic.com": anthropic-api-key
generativelanguage.googleapis.com: gemini-api-key
api.mistral.ai: mistral-api-key
serviceAuth:
openai-api-key: { headerName: authorization, valueFormat: "Bearer %s" }
anthropic-api-key: { headerName: x-api-key }
gemini-api-key: { headerName: x-goog-api-key }
mistral-api-key: { headerName: authorization, valueFormat: "Bearer %s" }

providers:
- name: openai-codex
- name: openai
credentials: [openai-api-key]
authFiles: [agent/auth.json]
authSession:
mode: any
checks:
- { file: agent/auth.json, type: json_pointer_non_null, pointer: /openai }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These five pointers are the substance of the PR and a reviewer can't check them against anything in-tree. A wrong key fails silently: checkProviderSession just reports "no session", which shows up as the OAuth port being auto-mapped forever rather than an error. Can you link the upstream auth.json shape you verified against?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pointers are correct:

- name: openai-codex
authFiles: [agent/auth.json]
Comment thread
xai marked this conversation as resolved.
authSession:
checks:
- { file: agent/auth.json, type: json_pointer_non_null, pointer: /openai-codex }
oauthPorts: [{ port: "1455" }]
- name: anthropic
credentials: [anthropic-api-key]
- name: gemini
authFiles: [agent/auth.json]
authSession:
checks:
- { file: agent/auth.json, type: json_pointer_non_null, pointer: /anthropic }
oauthPorts:
- { port: "53692", autoHintWhenNoSession: false, requireMappingWhenNoCredentials: false }
- name: google
credentials: [gemini-api-key]
authFiles: [agent/auth.json]
authSession:
checks:
- { file: agent/auth.json, type: json_pointer_non_null, pointer: /google }
- name: mistral
credentials: [mistral-api-key]
authFiles: [agent/auth.json]
authSession:
checks:
- { file: agent/auth.json, type: json_pointer_non_null, pointer: /mistral }
1 change: 1 addition & 0 deletions internal/app/build_permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func collectAppRootModeIssues(appRoot string) ([]string, error) {
}
for _, rel := range []string{
filepath.ToSlash(filepath.Join("runtime-assets", "auth-reconcile.sh")),
filepath.ToSlash(filepath.Join("runtime-assets", "kit-init.sh")),
filepath.ToSlash(filepath.Join("runtime-assets", "net.sh")),
filepath.ToSlash(filepath.Join("runtime-assets", "tmux-session.conf")),
} {
Expand Down
2 changes: 2 additions & 0 deletions internal/app/build_permissions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func TestCollectAppRootModeIssuesReportsRestrictiveAssets(t *testing.T) {
writeModeTestFile(t, filepath.Join(root, "runtime-assets", "build-scripts", "lib", "common.sh"), 0o640)
writeModeTestFile(t, filepath.Join(root, "runtime-assets", "build-scripts", "bin", "helper"), 0o644)
writeModeTestFile(t, filepath.Join(root, "runtime-assets", "auth-reconcile.sh"), 0o640)
writeModeTestFile(t, filepath.Join(root, "runtime-assets", "kit-init.sh"), 0o640)
writeModeTestFile(t, filepath.Join(root, "runtime-assets", "net.sh"), 0o644)
writeModeTestFile(t, filepath.Join(root, "runtime-assets", "tmux-session.conf"), 0o644)
writeModeTestFile(t, filepath.Join(root, "extensions", "tools", "claude", "spec.yaml"), 0o640)
Expand All @@ -35,6 +36,7 @@ func TestCollectAppRootModeIssuesReportsRestrictiveAssets(t *testing.T) {
"runtime-assets/build-scripts/lib/common.sh lacks world read/execute permission",
"runtime-assets/build-scripts/bin/helper lacks world execute permission",
"runtime-assets/auth-reconcile.sh lacks world read permission",
"runtime-assets/kit-init.sh lacks world read permission",
"extensions/tools/claude/spec.yaml lacks world read permission",
"extensions/tools/claude/install.sh lacks world execute permission",
} {
Expand Down
20 changes: 19 additions & 1 deletion internal/config/testdata/golden/tool-ext-pi.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"type": "sandbox",
"name": "pi",
"description": "Anthropic Claude Pi",
"description": "A minimal terminal coding harness",
"default_included": true,
"priority": 100,
"secrets": {
Expand Down Expand Up @@ -32,6 +32,20 @@
}
}
},
"mistral-api-key": {
"env_vars": [
"MISTRAL_API_KEY"
],
"release": {
"http": {
"hosts": [
"api.mistral.ai"
],
"header": "authorization",
"format": "Bearer %s"
}
}
},
"openai-api-key": {
"env_vars": [
"OPENAI_API_KEY"
Expand All @@ -47,5 +61,9 @@
}
}
}
},
"environment": {
"PI_SKIP_VERSION_CHECK": "1",
"PI_TELEMETRY": "0"
}
}
96 changes: 91 additions & 5 deletions internal/config/testdata/golden/tool-pi.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,31 @@
],
"providers": [
{
"name": "openai-codex",
"name": "openai",
"credential_secrets": [
"openai-api-key"
],
"auth_files": [
"agent/auth.json"
],
"auth_session": {
"mode": "any",
"mode": "",
"checks": [
{
"file": "agent/auth.json",
"type": "json_pointer_non_null",
"pointer": "/openai"
}
]
}
},
{
"name": "openai-codex",
"auth_files": [
"agent/auth.json"
],
"auth_session": {
"mode": "",
"checks": [
{
"file": "agent/auth.json",
Expand All @@ -52,13 +68,65 @@
"name": "anthropic",
"credential_secrets": [
"anthropic-api-key"
],
"auth_files": [
"agent/auth.json"
],
"auth_session": {
"mode": "",
"checks": [
{
"file": "agent/auth.json",
"type": "json_pointer_non_null",
"pointer": "/anthropic"
}
]
},
"oauth_ports": [
{
"port": "53692",
"auto_hint_when_no_session": false,
"require_mapping_when_no_credentials": false
}
]
},
{
"name": "gemini",
"name": "google",
"credential_secrets": [
"gemini-api-key"
]
],
"auth_files": [
"agent/auth.json"
],
"auth_session": {
"mode": "",
"checks": [
{
"file": "agent/auth.json",
"type": "json_pointer_non_null",
"pointer": "/google"
}
]
}
},
{
"name": "mistral",
"credential_secrets": [
"mistral-api-key"
],
"auth_files": [
"agent/auth.json"
],
"auth_session": {
"mode": "",
"checks": [
{
"file": "agent/auth.json",
"type": "json_pointer_non_null",
"pointer": "/mistral"
}
]
}
}
],
"secrets": {
Expand Down Expand Up @@ -89,6 +157,20 @@
}
}
},
"mistral-api-key": {
"env_vars": [
"MISTRAL_API_KEY"
],
"release": {
"http": {
"hosts": [
"api.mistral.ai"
],
"header": "authorization",
"format": "Bearer %s"
}
}
},
"openai-api-key": {
"env_vars": [
"OPENAI_API_KEY"
Expand All @@ -107,5 +189,9 @@
},
"host_config_dir": "",
"host_credentials_file": "",
"host_oauth_json": ""
"host_oauth_json": "",
"environment": {
"PI_SKIP_VERSION_CHECK": "1",
"PI_TELEMETRY": "0"
}
}
1 change: 1 addition & 0 deletions runtime-assets/microvm/alpine/build-bundle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ cp /src/runtime-assets/auth-reconcile.sh "$root/usr/local/share/enclave/auth-rec
cp /src/runtime-assets/net.sh "$root/usr/local/share/enclave/net.sh"
cp /src/runtime-assets/kit-init.sh "$root/usr/local/share/enclave/kit-init.sh"
chmod 0755 "$root/usr/local/bin/entrypoint.sh" "$root/usr/local/share/enclave/auth-reconcile.sh" "$root/usr/local/share/enclave/net.sh"
chmod 0644 "$root/usr/local/share/enclave/kit-init.sh"
Comment thread
xai marked this conversation as resolved.

if [ -d /src/docs ]; then cp -a /src/docs "$root/usr/share/doc/enclave/docs"; fi

Expand Down
Loading