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
12 changes: 10 additions & 2 deletions .agents/skills/openshell-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ This creates a sandbox whose canonical main process is `/bin/bash -l` and
attaches your terminal to that retained process. Add `--detach` to return after
the sandbox becomes ready without attaching.

An explicit trailing command is foreground even when stdin or stdout is not a
terminal. The CLI streams its stdout and stderr and returns its exact exit
status. Exit code 0 leaves a retained sandbox in `Completed`; nonzero leaves it
in `Error` with `MainProcessFailed`. Use `--no-keep` to delete either result
after output drains, or `--detach` for a long-running service.

When supplying `--name`, use a portable DNS-1123 label: at most 63 lowercase alphanumeric or `-` characters, beginning and ending with an alphanumeric character. The Kubernetes driver rejects uppercase letters, underscores, dots, and other names that cannot become Kubernetes resource labels.

**Shortcut for known tools**: When the trailing command is a recognized tool, the CLI auto-creates the required provider from local credentials:
Expand Down Expand Up @@ -236,7 +242,7 @@ Key flags:
- `--approval-mode manual|auto`: Control handling of agent-authored policy proposals; `manual` is the default
- `--upload <PATH>[:<DEST>]`: Upload local files into the container working directory or an explicit destination
- `--no-git-ignore`: Disable `.gitignore` filtering for uploads
- `--no-keep`: Delete the sandbox after the initial command or shell exits
- `--no-keep`: Delete the sandbox after main output and the exit result drain
- `--detach`: Start the canonical main process without attaching
- `--forward [BIND_ADDRESS:]PORT`: Forward a local port and keep the sandbox alive
- `--editor vscode|cursor`: Open a remote editor after creation and keep the sandbox alive
Expand Down Expand Up @@ -351,7 +357,9 @@ openshell sandbox start [name]
Both commands default to the last-used sandbox. Stop stops background
forwards and waits for `Stopped`; start waits for `Ready`. Connect, exec,
file transfer, forwarding, and exposed services are unavailable while
stopped. Delete remains the operation that removes retained state.
stopped or completed. Starting a retained `Completed` or
`Error/MainProcessFailed` sandbox launches a fresh canonical-main instance.
Delete remains the operation that removes retained state.

---

Expand Down
12 changes: 8 additions & 4 deletions .agents/skills/openshell-cli/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,13 +207,16 @@ identity provider. Requires an authenticated gateway connection.
Create a sandbox through the selected gateway and launch its canonical main
process. By default, the CLI attaches to that retained process after the
sandbox becomes ready. A trailing command defines the canonical main process;
without one, the default is `/bin/bash -l` with a PTY.
without one, the default is `/bin/bash -l` with a PTY. Explicit commands remain
foreground in non-interactive automation: stdout and stderr stream to the
caller and the CLI returns the command's exact status. Exit 0 leaves
`Completed`; nonzero leaves `Error/MainProcessFailed`.

| Flag | Description |
|------|-------------|
| `--name <NAME>` | Sandbox name (auto-generated if omitted) |
| `--from <SOURCE>` | Community name, Dockerfile path, directory, or image reference (BYOC) |
| `--no-keep` | Delete the sandbox after the initial command or shell exits |
| `--no-keep` | Delete the sandbox after main output and the result drain |
| `--detach` | Start the canonical main process without attaching |
| `--editor vscode|cursor` | Launch a remote editor and keep the sandbox alive |
| `--gpu [COUNT]` | Request the driver's default GPU selection or a specific count |
Expand Down Expand Up @@ -269,8 +272,9 @@ and waits for the `Stopped` phase.

### `openshell sandbox start [name]`

Start a stopped sandbox and wait for `Ready`. The name defaults to the
last-used sandbox.
Start a stopped, failed, or completed sandbox and wait for `Ready`. This
launches a fresh canonical-main instance. The name defaults to the last-used
sandbox.

### `openshell sandbox exec [OPTIONS] -- COMMAND...`

Expand Down
6 changes: 6 additions & 0 deletions architecture/compute-runtimes.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ The gateway persists lifecycle intent before mutating compute:
Ready -> Stopping -> Stopped -> Starting -> Ready
```

A canonical main process that exits successfully follows `Ready -> Completed`.
A nonzero or signal-normalized result follows `Ready -> Error` with a
`MainProcessFailed` condition. Both retained results may be started explicitly,
which creates a fresh main-process instance. Drivers must not automatically
restart a completed or failed canonical process.

`StopSandbox` and `StartSandbox` are idempotent driver operations. Stop
retains the driver resource and its persistent workspace boundary while making
exec, SSH, forwarding, and exposed services unavailable. Start reactivates the
Expand Down
10 changes: 7 additions & 3 deletions architecture/gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ workloads.
- Coordinate supervisor relay sessions for connect, exec, file sync, and
service forwarding.
- Persist the canonical main-process instance ID and normalized exit code on
sandbox status. Any main process exit transitions the sandbox to `Error`,
including exit code zero.
sandbox status. Exit code zero transitions the sandbox to `Completed`;
nonzero results transition it to `Error/MainProcessFailed`. Infrastructure
failures also use `Error`, with a distinct reason and no fabricated command
result.

The gateway does not enforce agent network policy at request time. That happens
inside each sandbox, where the supervisor and proxy can observe local process
Expand All @@ -26,7 +28,9 @@ identity.
The live supervisor session is the readiness authority for its main-process
instance. The supervisor reports its normalized result through the
sandbox-authenticated `ReportMainProcessExit` RPC, and the gateway rejects
results from stale instance IDs.
results from stale instance IDs. The process supervisor keeps the main SSH
session alive until an attached foreground client receives the terminal result
or a bounded detached timeout expires, then reports the result and closes.

## Protocol and Auth

Expand Down
10 changes: 6 additions & 4 deletions architecture/sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,9 @@ engine with a gateway policy revision.
re-evaluate.
- If the supervisor relay drops, the sandbox can keep running, but connect and
exec operations fail until the supervisor registers again.
- If the canonical main process exits, including with code 0, the supervisor
reports its normalized exit code before shutdown. The gateway persists the
code on sandbox status, records `MainProcessExited`, and makes the sandbox
terminal `Error`; runtime restart policies must not replace the process.
- If the canonical main process exits, the supervisor drains its retained main
output and reports the normalized result before shutdown. Exit code 0 records
`Completed/MainProcessCompleted`; nonzero and signal-normalized exits record
`Error/MainProcessFailed`. Infrastructure failures also use `Error`, with a
distinct condition reason and no fabricated canonical-process result. Runtime
restart policies must not replace the canonical process.
1 change: 1 addition & 0 deletions crates/openshell-cli/src/commands/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pub fn phase_name(phase: i32) -> &'static str {
Ok(SandboxPhase::Stopping) => "Stopping",
Ok(SandboxPhase::Stopped) => "Stopped",
Ok(SandboxPhase::Starting) => "Starting",
Ok(SandboxPhase::Completed) => "Completed",
Ok(SandboxPhase::Unknown) | Err(_) => "Unknown",
}
}
Expand Down
12 changes: 10 additions & 2 deletions crates/openshell-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3063,7 +3063,7 @@ async fn run_async() -> Result<()> {
let endpoint = &ctx.endpoint;
let mut tls = tls.with_gateway_name(&ctx.name);
apply_auth(&mut tls, &ctx.name);
Box::pin(run::sandbox_create(
let exit_code = Box::pin(run::sandbox_create(
endpoint,
&ctx.name,
run::SandboxCreateConfig {
Expand Down Expand Up @@ -3092,6 +3092,9 @@ async fn run_async() -> Result<()> {
&tls,
))
.await?;
if exit_code != 0 {
std::process::exit(exit_code);
}
}
SandboxCommands::Upload {
name,
Expand Down Expand Up @@ -3217,7 +3220,12 @@ async fn run_async() -> Result<()> {
)
.await?;
} else {
run::sandbox_connect(endpoint, &name, &tls, &cli.workspace).await?;
let exit_code =
run::sandbox_connect(endpoint, &name, &tls, &cli.workspace)
.await?;
if exit_code != 0 {
std::process::exit(exit_code);
}
}
let _ = save_last_sandbox(&ctx.name, &cli.workspace, &name);
}
Expand Down
Loading
Loading