Skip to content

Commit 2e8697c

Browse files
committed
feat(cli): use wait flag for sandbox deletion
Signed-off-by: Evan Lezar <elezar@nvidia.com>
1 parent 757fff6 commit 2e8697c

6 files changed

Lines changed: 79 additions & 43 deletions

File tree

‎.agents/skills/openshell-cli/SKILL.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,15 +359,15 @@ openshell logs my-sandbox --since 5m
359359
openshell sandbox delete my-sandbox
360360
openshell sandbox delete sandbox-1 sandbox-2 sandbox-3 # Multiple at once
361361
openshell sandbox delete --all
362-
openshell sandbox delete my-sandbox --no-wait # Async acknowledgment only
362+
openshell sandbox delete my-sandbox --wait=false # Async acknowledgment only
363363
```
364364

365365
Delete waits for terminal absence by default, so a following workspace delete
366-
can run immediately. Use `--no-wait` only when the caller does not depend on
366+
can run immediately. Use `--wait=false` only when the caller does not depend on
367367
cleanup completion. Override the default 300-second per-sandbox lifecycle
368368
timeout with a positive `--timeout <seconds>` value or
369369
`OPENSHELL_LIFECYCLE_TIMEOUT`; a timeout reports an error while gateway
370-
deletion continues asynchronously.
370+
deletion continues asynchronously. The timeout is ignored when `--wait=false`.
371371

372372
### Stop and start sandboxes
373373

‎.agents/skills/openshell-cli/cli-reference.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ port forwards and waits for terminal absence by default.
276276
| Flag | Default | Description |
277277
|------|---------|-------------|
278278
| `--all` | false | Delete every sandbox in the selected workspace |
279-
| `--no-wait` | false | Return after the gateway accepts deletion |
279+
| `--wait <true\|false>` | true | Wait for terminal deletion; `false` returns after gateway acknowledgement |
280280
| `--timeout <SECONDS>` | 300 | Positive per-sandbox terminal deletion wait timeout |
281281

282282
### `openshell sandbox stop [name]`

‎crates/openshell-cli/src/main.rs‎

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,9 +1544,17 @@ enum SandboxCommands {
15441544
#[arg(long, conflicts_with = "names")]
15451545
all: bool,
15461546

1547-
/// Return after the gateway accepts the deletion request.
1548-
#[arg(long, conflicts_with = "timeout")]
1549-
no_wait: bool,
1547+
/// Wait for each sandbox to reach terminal deletion.
1548+
///
1549+
/// Pass `--wait=false` to return after the gateway accepts deletion.
1550+
#[arg(
1551+
long,
1552+
default_value_t = true,
1553+
default_missing_value = "true",
1554+
num_args = 0..=1,
1555+
action = clap::ArgAction::Set,
1556+
)]
1557+
wait: bool,
15501558

15511559
/// Positive seconds to wait for each sandbox to reach terminal deletion.
15521560
#[arg(long, value_name = "SECONDS", value_parser = clap::value_parser!(u64).range(1..))]
@@ -3217,7 +3225,7 @@ async fn run_async() -> Result<()> {
32173225
SandboxCommands::Delete {
32183226
names,
32193227
all,
3220-
no_wait,
3228+
wait,
32213229
timeout,
32223230
} => {
32233231
run::sandbox_delete(
@@ -3228,7 +3236,7 @@ async fn run_async() -> Result<()> {
32283236
&tls,
32293237
&ctx.name,
32303238
run::SandboxDeleteOptions {
3231-
no_wait,
3239+
wait,
32323240
timeout: timeout.map(Duration::from_secs),
32333241
},
32343242
)
@@ -4583,26 +4591,37 @@ mod tests {
45834591
}
45844592

45854593
#[test]
4586-
fn sandbox_delete_waits_by_default_and_accepts_async_options() {
4594+
fn sandbox_delete_waits_by_default_and_accepts_wait_options() {
45874595
let default_delete = Cli::try_parse_from(["openshell", "sandbox", "delete", "demo"])
45884596
.expect("sandbox delete should parse");
45894597
assert!(matches!(
45904598
default_delete.command,
45914599
Some(Commands::Sandbox {
45924600
command: Some(SandboxCommands::Delete {
4593-
no_wait: false,
4601+
wait: true,
45944602
timeout: None,
45954603
..
45964604
})
45974605
})
45984606
));
45994607

4600-
let no_wait = Cli::try_parse_from(["openshell", "sandbox", "delete", "demo", "--no-wait"])
4601-
.expect("sandbox delete --no-wait should parse");
4608+
let async_delete =
4609+
Cli::try_parse_from(["openshell", "sandbox", "delete", "demo", "--wait=false"])
4610+
.expect("sandbox delete --wait=false should parse");
46024611
assert!(matches!(
4603-
no_wait.command,
4612+
async_delete.command,
46044613
Some(Commands::Sandbox {
4605-
command: Some(SandboxCommands::Delete { no_wait: true, .. })
4614+
command: Some(SandboxCommands::Delete { wait: false, .. })
4615+
})
4616+
));
4617+
4618+
let explicit_wait =
4619+
Cli::try_parse_from(["openshell", "sandbox", "delete", "demo", "--wait"])
4620+
.expect("sandbox delete --wait should parse");
4621+
assert!(matches!(
4622+
explicit_wait.command,
4623+
Some(Commands::Sandbox {
4624+
command: Some(SandboxCommands::Delete { wait: true, .. })
46064625
})
46074626
));
46084627

@@ -4613,26 +4632,33 @@ mod tests {
46134632
timeout.command,
46144633
Some(Commands::Sandbox {
46154634
command: Some(SandboxCommands::Delete {
4616-
no_wait: false,
4635+
wait: true,
46174636
timeout: Some(42),
46184637
..
46194638
})
46204639
})
46214640
));
46224641

4623-
assert!(
4624-
Cli::try_parse_from([
4625-
"openshell",
4626-
"sandbox",
4627-
"delete",
4628-
"demo",
4629-
"--no-wait",
4630-
"--timeout",
4631-
"42",
4632-
])
4633-
.is_err(),
4634-
"--no-wait and --timeout should conflict"
4635-
);
4642+
let asynchronous = Cli::try_parse_from([
4643+
"openshell",
4644+
"sandbox",
4645+
"delete",
4646+
"demo",
4647+
"--wait=false",
4648+
"--timeout",
4649+
"42",
4650+
])
4651+
.expect("--wait=false should override the timeout");
4652+
assert!(matches!(
4653+
asynchronous.command,
4654+
Some(Commands::Sandbox {
4655+
command: Some(SandboxCommands::Delete {
4656+
wait: false,
4657+
timeout: Some(42),
4658+
..
4659+
})
4660+
})
4661+
));
46364662
assert!(
46374663
Cli::try_parse_from(["openshell", "sandbox", "delete", "demo", "--timeout", "0",])
46384664
.is_err(),

‎crates/openshell-cli/src/run.rs‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2454,14 +2454,23 @@ fn format_provider_attachment_table(providers: &[Provider], color: bool) -> Stri
24542454
}
24552455

24562456
/// Controls whether sandbox deletion waits for durable absence.
2457-
#[derive(Debug, Clone, Copy, Default)]
2457+
#[derive(Debug, Clone, Copy)]
24582458
pub struct SandboxDeleteOptions {
2459-
/// Return after the gateway acknowledges an asynchronous delete.
2460-
pub no_wait: bool,
2459+
/// Wait for terminal absence after deletion is acknowledged.
2460+
pub wait: bool,
24612461
/// Override the per-sandbox lifecycle timeout. The environment/default applies when unset.
24622462
pub timeout: Option<Duration>,
24632463
}
24642464

2465+
impl Default for SandboxDeleteOptions {
2466+
fn default() -> Self {
2467+
Self {
2468+
wait: true,
2469+
timeout: None,
2470+
}
2471+
}
2472+
}
2473+
24652474
/// Delete a sandbox by name, or all sandboxes when `all` is true.
24662475
pub async fn sandbox_delete(
24672476
server: &str,
@@ -2529,7 +2538,7 @@ pub async fn sandbox_delete(
25292538
let deletion_pending = response.into_inner().deleted;
25302539
clear_last_sandbox_if_matches(gateway, workspace, name);
25312540

2532-
if options.no_wait && deletion_pending {
2541+
if !options.wait && deletion_pending {
25332542
println!(
25342543
"{} Deletion requested for sandbox {name}",
25352544
"✓".green().bold()

‎crates/openshell-cli/tests/sandbox_create_lifecycle_integration.rs‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,7 +1251,7 @@ async fn sandbox_delete_waits_for_terminal_absence() {
12511251
&test_tls(&server),
12521252
"openshell",
12531253
run::SandboxDeleteOptions {
1254-
no_wait: false,
1254+
wait: true,
12551255
timeout: Some(Duration::from_secs(2)),
12561256
},
12571257
)
@@ -1269,7 +1269,7 @@ async fn sandbox_delete_waits_for_terminal_absence() {
12691269
}
12701270

12711271
#[tokio::test]
1272-
async fn sandbox_delete_no_wait_returns_without_polling() {
1272+
async fn sandbox_delete_wait_false_returns_without_polling() {
12731273
let server = run_server().await;
12741274
let xdg_dir = tempfile::tempdir().unwrap();
12751275
let fake_ssh_dir = tempfile::tempdir().unwrap();
@@ -1288,12 +1288,12 @@ async fn sandbox_delete_no_wait_returns_without_polling() {
12881288
&test_tls(&server),
12891289
"openshell",
12901290
run::SandboxDeleteOptions {
1291-
no_wait: true,
1292-
timeout: None,
1291+
wait: false,
1292+
timeout: Some(Duration::from_secs(1)),
12931293
},
12941294
)
12951295
.await
1296-
.expect("--no-wait behavior should return after acknowledgment");
1296+
.expect("--wait=false behavior should return after acknowledgment");
12971297

12981298
assert_eq!(
12991299
server
@@ -1325,7 +1325,7 @@ async fn sandbox_delete_reports_timeout_and_unexpected_lookup_errors() {
13251325
&test_tls(&server),
13261326
"openshell",
13271327
run::SandboxDeleteOptions {
1328-
no_wait: false,
1328+
wait: true,
13291329
timeout: Some(Duration::from_millis(150)),
13301330
},
13311331
)
@@ -1350,7 +1350,7 @@ async fn sandbox_delete_reports_timeout_and_unexpected_lookup_errors() {
13501350
&test_tls(&server),
13511351
"openshell",
13521352
run::SandboxDeleteOptions {
1353-
no_wait: false,
1353+
wait: true,
13541354
timeout: Some(Duration::from_secs(1)),
13551355
},
13561356
)

‎docs/sandboxes/manage-sandboxes.mdx‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -534,11 +534,12 @@ openshell sandbox delete my-sandbox
534534
535535
The CLI waits until the sandbox is durably absent before it reports success, so
536536
you can safely compose deletion with operations such as workspace deletion. Use
537-
`--no-wait` to return after the gateway accepts the asynchronous request. Set
538-
`--timeout <seconds>` with a positive value to override the terminal wait
537+
`--wait=false` to return after the gateway accepts the asynchronous request.
538+
Set `--timeout <seconds>` with a positive value to override the terminal wait
539539
timeout for each sandbox; otherwise the CLI uses
540540
`OPENSHELL_LIFECYCLE_TIMEOUT`, which defaults to 300 seconds. A timeout returns
541-
an error while deletion continues asynchronously.
541+
an error while deletion continues asynchronously. The timeout is ignored when
542+
`--wait=false`.
542543
543544
## Sandbox Lifecycle
544545

0 commit comments

Comments
 (0)