Skip to content
Closed
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
15 changes: 12 additions & 3 deletions crates/mvm-agentd/src/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1426,9 +1426,18 @@ mod tests {
let argv = build_console_argv(&request.argv).expect("argv");
let (child_pid, mut master) = spawn_shell(&request, &argv).expect("spawn shell on a pty");
let mut output = Vec::new();
master
.read_to_end(&mut output)
.expect("read the pty master");
// EIO is how a pty master reports that every holder of the slave side
// has closed it; it ends the stream exactly like EOF (same contract
// as `pump_output`), so a fast-exiting shell must not fail the read.
loop {
let mut buf = [0u8; 4096];
match master.read(&mut buf) {
Ok(0) => break,
Ok(n) => output.extend_from_slice(&buf[..n]),
Err(error) if error.kind() == std::io::ErrorKind::Interrupted => {}
Err(_) => break,
}
}
// SAFETY: child_pid is a live child of this test process; waitpid
// reaps it and touches no memory.
unsafe {
Expand Down
18 changes: 18 additions & 0 deletions crates/mvm-conformance/tests/steps/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,24 @@ fn scratch_working_directory(world: &mut CliWorld) {
world.scratch_dir = Some(tempfile::tempdir().expect("create scratch working directory"));
}

/// The `./policy/api-group.toml` group the policies guide validates: the
/// documented example itself, staged verbatim, so the scenario exercises the
/// exact invocation a reader pastes.
#[given(expr = "an authored policy group file in the scratch directory")]
fn authored_policy_group_in_scratch(world: &mut CliWorld) {
let scratch = world
.scratch_dir
.as_ref()
.expect("`Given a scratch working directory` must run before this step");
let policy_dir = scratch.path().join("policy");
fs::create_dir(&policy_dir).expect("create policy fixture directory");
fs::write(
policy_dir.join("api-group.toml"),
"description = \"Read the GitHub API\"\n\n[network]\nallow = [\"api.github.com:443\"]\n",
)
.expect("write policy group fixture");
}

#[when(expr = "I run the conformance runner with {string}")]
fn run_conformance_runner(world: &mut CliWorld, args: String) {
run_conformance_child(world, &args, None);
Expand Down
8 changes: 8 additions & 0 deletions features/suites/s29_doc_examples/doc_examples.feature
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ Feature: Documented examples work
And the output contains "identity.signer_id"
And the output contains "hardware providers:"

Scenario: policy validate checks an authored group file offline
Given an isolated mvm home
And a scratch working directory
And an authored policy group file in the scratch directory
When I run mvmctl in the scratch directory with "policy validate ./policy/api-group.toml --verbose"
Then the command exits with code 0
And the output contains "policy is valid"

Scenario: a sealed audit fixture has a correlated JSON verdict
Given an isolated mvm home
And a sealed audit fixture for session "sha256:1111111111111111111111111111111111111111111111111111111111111111"
Expand Down
10 changes: 3 additions & 7 deletions features/suites/s29_doc_examples/docs_coverage.toml
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ covered = [
"mvmctl pack info runtime/node",
"mvmctl pack ls",
"mvmctl pack verify runtime/node",
"mvmctl policy validate ./pack-sources/agent/codex/pack/profile.toml",
"mvmctl policy validate ./pack-sources/runtime/node/pack/group.toml",
"mvmctl policy validate ./policy/api-group.toml",
"mvmctl pool warm --image alpine",
"mvmctl run -- npm test",
"mvmctl run -- uname -a",
Expand Down Expand Up @@ -514,11 +517,7 @@ uncovered = [
"mvmctl policy resolve agent-apis -o agent.json",
"mvmctl policy show",
"mvmctl policy show agent-apis",
"mvmctl policy validate",
"mvmctl policy validate ./pack-sources/agent/codex/pack/profile.toml",
"mvmctl policy validate ./pack-sources/runtime/node/pack/group.toml",
"mvmctl pool warm --flake ./flake.nix",
"mvmctl prepare",
"mvmctl pull agent/claude",
"mvmctl pull agent/claude@1.0.1",
"mvmctl pull agent/codex",
Expand Down Expand Up @@ -603,7 +602,4 @@ uncovered = [
"mvmctl why --secret anthropic --profile agent/goose",
"mvmctl why --secret openai --profile agent/codex",
"mvmctl why --secret openai --profile agent/opencode",
"mvmctl why --tool mvm.machine.list",
"mvmctl why --tool mvm.machine.stop",
"mvmctl why --tool shell --profile agent-apis",
]
4 changes: 2 additions & 2 deletions public/src/content/docs/sdk/operations-cookbook.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ mvmctl run --mode live ./sandbox.py
<TabItem label="TypeScript">

```ts
import { Sandbox, network, resources, secret } from "@runmvm/mvm";
import { Sandbox, literal, network, resources, secret } from "@runmvm/mvm";

using sandbox = Sandbox.create("node-22", {
workloadId: "agent-tool",
Expand All @@ -89,7 +89,7 @@ using sandbox = Sandbox.create("node-22", {
sandbox.files.write("/app/main.js", "console.log('hello from mvm')");
sandbox.commands.start(["node", "/app/main.js"], {
env: {
MODE: "dev",
MODE: literal("dev"),
API_KEY: secret("api-key", { type: "bearer", hosts: ["api.example.com"] }),
},
});
Expand Down