claude -p …, then reads stdout or files under /workspace.
Complete Get started first so Anthropic egress works.
Prerequisites
export GRAVIXLAYER_API_KEY="your-gravixlayer-api-key"
export ANTHROPIC_API_KEY="sk-ant-..."
export ANTHROPIC_PROVIDER_ID="..." # from get-started
export CLAUDE_NETWORK_POLICY_ID="..."
claude.
For unattended runs, pass --dangerously-skip-permissions. That is safe only because the agent is inside the sandbox — still keep your network allowlist tight and set a timeout on every command.
Generate a small project
Creates a sandbox, asks Claude to write a tiny Go HTTP server, then lists the files it produced.import os
from gravixlayer import GravixLayer
client = GravixLayer() # defaults to cloud="aws", region="us-east-1"
sandbox = client.runtime.create(
template="claude",
providers=[os.environ["ANTHROPIC_PROVIDER_ID"]],
network_policy_ids=[os.environ["CLAUDE_NETWORK_POLICY_ID"]],
env_vars={"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]},
timeout=1800,
)
print("runtime_id:", sandbox.runtime_id)
result = sandbox.run_cmd(
command="claude",
args=[
"--dangerously-skip-permissions",
"-p",
"Create a hello-world HTTP server in Go under /workspace/hello. "
"It should listen on :8080 and respond OK to GET /. "
"When finished, print the file paths you created.",
"--output-format",
"text",
],
working_dir="/workspace",
timeout=900,
)
print(result.stdout)
print("exit_code:", result.exit_code)
print(sandbox.run_cmd(command="find", args=["/workspace/hello", "-type", "f"], timeout=60).stdout)
sandbox.kill()
import { GravixLayer } from 'gravixlayer';
const client = new GravixLayer(); // defaults to cloud="aws", region="us-east-1"
const sandbox = await client.runtime.create({
template: 'claude',
providers: [process.env['ANTHROPIC_PROVIDER_ID']!],
networkPolicyIds: [process.env['CLAUDE_NETWORK_POLICY_ID']!],
envVars: { ANTHROPIC_API_KEY: process.env['ANTHROPIC_API_KEY']! },
timeout: 1800,
});
console.log('runtime_id:', sandbox.runtimeId);
const result = await sandbox.runCmd('claude', {
args: [
'--dangerously-skip-permissions',
'-p',
'Create a hello-world HTTP server in Go under /workspace/hello. It should listen on :8080 and respond OK to GET /. When finished, print the file paths you created.',
'--output-format',
'text',
],
workingDir: '/workspace',
timeout: 900,
});
console.log(result.stdout);
console.log('exit_code:', result.exitCode);
console.log((await sandbox.runCmd('find', { args: ['/workspace/hello', '-type', 'f'], timeout: 60 })).stdout);
await sandbox.kill();
RT=$(gravixlayer runtime create \
--template claude \
--provider "$ANTHROPIC_PROVIDER_ID" \
--network-policy "$CLAUDE_NETWORK_POLICY_ID" \
--env "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" \
--timeout 1800 \
--wait \
--output json | jq -r '.runtime_id')
gravixlayer runtime exec "$RT" --timeout 900 --workdir /workspace -- \
claude --dangerously-skip-permissions \
-p 'Create a hello-world HTTP server in Go under /workspace/hello that listens on :8080.' \
--output-format text
gravixlayer runtime exec "$RT" -- find /workspace/hello -type f
gravixlayer runtime kill "$RT" -y
API="https://api.gravixlayer.ai/v1"
AUTH=(-H "Authorization: Bearer $GRAVIXLAYER_API_KEY" -H "Content-Type: application/json")
RT=$(curl -sS -X POST "$API/agents/runtime" "${AUTH[@]}" \
-d "{
\"template\": \"claude\",
\"cloud\": \"aws\",
\"region\": \"us-east-1\",
\"timeout\": 1800,
\"providers\": [\"$ANTHROPIC_PROVIDER_ID\"],
\"network_policy_ids\": [\"$CLAUDE_NETWORK_POLICY_ID\"],
\"env_vars\": {\"ANTHROPIC_API_KEY\": \"$ANTHROPIC_API_KEY\"}
}" | jq -r '.runtime_id')
# Wait until status is running, then (timeout is milliseconds):
curl -sS -X POST "$API/agents/runtime/$RT/commands/run" "${AUTH[@]}" \
-d '{
"command": "claude",
"args": [
"--dangerously-skip-permissions",
"-p", "Create a hello-world HTTP server in Go under /workspace/hello that listens on :8080.",
"--output-format", "text"
],
"working_dir": "/workspace",
"timeout": 900000
}' | jq .
curl -sS -X DELETE "$API/agents/runtime/$RT" \
-H "Authorization: Bearer $GRAVIXLAYER_API_KEY"
command= + args= (or CLI --) so prompts with spaces or quotes do not break the shell.
Plan, then implement (session resume)
--output-format json includes a session_id. Keep the same sandbox and pass --resume for the next turn (CLI reference).
import json
import os
from gravixlayer import GravixLayer
client = GravixLayer() # defaults to cloud="aws", region="us-east-1"
sandbox = client.runtime.create(
template="claude",
providers=[os.environ["ANTHROPIC_PROVIDER_ID"]],
network_policy_ids=[os.environ["CLAUDE_NETWORK_POLICY_ID"]],
env_vars={"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]},
timeout=1800,
)
# Seed a tiny project so the plan has something real to look at
sandbox.file.write(
"/workspace/app.py",
"def add(a, b):\n return a + b\n\nif __name__ == '__main__':\n print(add(2, 2))\n",
)
first = sandbox.run_cmd(
command="claude",
args=[
"--dangerously-skip-permissions",
"--output-format",
"json",
"-p",
"Read /workspace/app.py and propose a one-step improvement (e.g. add a docstring or a tiny test).",
],
timeout=900,
)
print("plan exit_code:", first.exit_code)
payload = json.loads(first.stdout)
session_id = payload["session_id"]
print("session_id:", session_id)
follow = sandbox.run_cmd(
command="claude",
args=[
"--dangerously-skip-permissions",
"--resume",
session_id,
"-p",
"Implement that one-step improvement now.",
],
timeout=900,
)
print(follow.stdout)
print(sandbox.file.read("/workspace/app.py").content)
sandbox.kill()
import { GravixLayer } from 'gravixlayer';
const client = new GravixLayer(); // defaults to cloud="aws", region="us-east-1"
const sandbox = await client.runtime.create({
template: 'claude',
providers: [process.env['ANTHROPIC_PROVIDER_ID']!],
networkPolicyIds: [process.env['CLAUDE_NETWORK_POLICY_ID']!],
envVars: { ANTHROPIC_API_KEY: process.env['ANTHROPIC_API_KEY']! },
timeout: 1800,
});
await sandbox.file.write(
'/workspace/app.py',
"def add(a, b):\n return a + b\n\nif __name__ == '__main__':\n print(add(2, 2))\n",
);
const first = await sandbox.runCmd('claude', {
args: [
'--dangerously-skip-permissions',
'--output-format',
'json',
'-p',
'Read /workspace/app.py and propose a one-step improvement (e.g. add a docstring or a tiny test).',
],
timeout: 900,
});
console.log('plan exit_code:', first.exitCode);
const payload = JSON.parse(first.stdout);
const sessionId = payload.session_id;
console.log('session_id:', sessionId);
const follow = await sandbox.runCmd('claude', {
args: [
'--dangerously-skip-permissions',
'--resume',
sessionId,
'-p',
'Implement that one-step improvement now.',
],
timeout: 900,
});
console.log(follow.stdout);
console.log((await sandbox.file.read('/workspace/app.py')).content);
await sandbox.kill();
RT=$(gravixlayer runtime create \
--template claude \
--provider "$ANTHROPIC_PROVIDER_ID" \
--network-policy "$CLAUDE_NETWORK_POLICY_ID" \
--env "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" \
--timeout 1800 \
--wait \
--output json | jq -r '.runtime_id')
cat <<'EOF' | gravixlayer runtime files write "$RT" /workspace/app.py
def add(a, b):
return a + b
if __name__ == '__main__':
print(add(2, 2))
EOF
SESSION=$(gravixlayer runtime exec "$RT" --timeout 900 --workdir /workspace --output json -- \
claude --dangerously-skip-permissions --output-format json \
-p 'Read /workspace/app.py and propose a one-step improvement (e.g. add a docstring or a tiny test).' \
| jq -r '.stdout' | jq -r '.session_id')
echo "session_id: $SESSION"
gravixlayer runtime exec "$RT" --timeout 900 --workdir /workspace -- \
claude --dangerously-skip-permissions --resume "$SESSION" \
-p 'Implement that one-step improvement now.'
gravixlayer runtime files cat "$RT" /workspace/app.py
gravixlayer runtime kill "$RT" -y
API="https://api.gravixlayer.ai/v1"
AUTH=(-H "Authorization: Bearer $GRAVIXLAYER_API_KEY" -H "Content-Type: application/json")
RT=$(curl -sS -X POST "$API/agents/runtime" "${AUTH[@]}" \
-d "{
\"template\": \"claude\",
\"cloud\": \"aws\",
\"region\": \"us-east-1\",
\"timeout\": 1800,
\"providers\": [\"$ANTHROPIC_PROVIDER_ID\"],
\"network_policy_ids\": [\"$CLAUDE_NETWORK_POLICY_ID\"],
\"env_vars\": {\"ANTHROPIC_API_KEY\": \"$ANTHROPIC_API_KEY\"}
}" | jq -r '.runtime_id')
curl -sS -X POST "$API/agents/runtime/$RT/files/write" "${AUTH[@]}" \
-d '{"path": "/workspace/app.py", "content": "def add(a, b):\n return a + b\n\nif __name__ == '\''__main__'\'':\n print(add(2, 2))\n"}' | jq .
# Wait until status is running, then (timeout is milliseconds):
SESSION=$(curl -sS -X POST "$API/agents/runtime/$RT/commands/run" "${AUTH[@]}" \
-d '{
"command": "claude",
"args": [
"--dangerously-skip-permissions",
"--output-format", "json",
"-p", "Read /workspace/app.py and propose a one-step improvement (e.g. add a docstring or a tiny test)."
],
"working_dir": "/workspace",
"timeout": 900000
}' | jq -r '.stdout' | jq -r '.session_id')
echo "session_id: $SESSION"
curl -sS -X POST "$API/agents/runtime/$RT/commands/run" "${AUTH[@]}" \
-d "{
\"command\": \"claude\",
\"args\": [
\"--dangerously-skip-permissions\",
\"--resume\", \"$SESSION\",
\"-p\", \"Implement that one-step improvement now.\"
],
\"working_dir\": \"/workspace\",
\"timeout\": 900000
}" | jq .
curl -sS -X DELETE "$API/agents/runtime/$RT" \
-H "Authorization: Bearer $GRAVIXLAYER_API_KEY"
Stream progress (stream-json)
import json
import os
from gravixlayer import GravixLayer
client = GravixLayer() # defaults to cloud="aws", region="us-east-1"
sandbox = client.runtime.create(
template="claude",
providers=[os.environ["ANTHROPIC_PROVIDER_ID"]],
network_policy_ids=[os.environ["CLAUDE_NETWORK_POLICY_ID"]],
env_vars={"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]},
timeout=1800,
)
sandbox.file.write("/workspace/README.md", "# demo\n\nA tiny sandbox project.\n")
def on_stdout(chunk: str) -> None:
for line in chunk.splitlines():
if not line.strip():
continue
try:
event = json.loads(line)
except json.JSONDecodeError:
print(line, end="")
continue
if event.get("type") == "result":
print("done:", event.get("subtype"), "ms=", event.get("duration_ms"))
result = sandbox.run_cmd(
command="claude",
args=[
"--dangerously-skip-permissions",
"--output-format",
"stream-json",
"-p",
"List files under /workspace and summarize README.md in one sentence.",
],
timeout=900,
on_stdout=on_stdout,
)
print("exit_code:", result.exit_code)
sandbox.kill()
import { GravixLayer } from 'gravixlayer';
const client = new GravixLayer(); // defaults to cloud="aws", region="us-east-1"
const sandbox = await client.runtime.create({
template: 'claude',
providers: [process.env['ANTHROPIC_PROVIDER_ID']!],
networkPolicyIds: [process.env['CLAUDE_NETWORK_POLICY_ID']!],
envVars: { ANTHROPIC_API_KEY: process.env['ANTHROPIC_API_KEY']! },
timeout: 1800,
});
await sandbox.file.write('/workspace/README.md', '# demo\n\nA tiny sandbox project.\n');
const result = await sandbox.runCmd('claude', {
args: [
'--dangerously-skip-permissions',
'--output-format',
'stream-json',
'-p',
'List files under /workspace and summarize README.md in one sentence.',
],
timeout: 900,
onStdout: (chunk) => {
for (const line of chunk.split('\n')) {
if (!line.trim()) continue;
try {
const event = JSON.parse(line);
if (event.type === 'result') {
console.log('done:', event.subtype, 'ms=', event.duration_ms);
}
} catch {
process.stdout.write(line);
}
}
},
});
console.log('exit_code:', result.exitCode);
await sandbox.kill();
RT=$(gravixlayer runtime create \
--template claude \
--provider "$ANTHROPIC_PROVIDER_ID" \
--network-policy "$CLAUDE_NETWORK_POLICY_ID" \
--env "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" \
--timeout 1800 \
--wait \
--output json | jq -r '.runtime_id')
echo '# demo' | gravixlayer runtime files write "$RT" /workspace/README.md
gravixlayer runtime exec "$RT" --stream --timeout 900 --workdir /workspace -- \
claude --dangerously-skip-permissions \
--output-format stream-json \
-p 'List files under /workspace and summarize README.md in one sentence.'
gravixlayer runtime kill "$RT" -y
API="https://api.gravixlayer.ai/v1"
AUTH=(-H "Authorization: Bearer $GRAVIXLAYER_API_KEY" -H "Content-Type: application/json")
RT=$(curl -sS -X POST "$API/agents/runtime" "${AUTH[@]}" \
-d "{
\"template\": \"claude\",
\"cloud\": \"aws\",
\"region\": \"us-east-1\",
\"timeout\": 1800,
\"providers\": [\"$ANTHROPIC_PROVIDER_ID\"],
\"network_policy_ids\": [\"$CLAUDE_NETWORK_POLICY_ID\"],
\"env_vars\": {\"ANTHROPIC_API_KEY\": \"$ANTHROPIC_API_KEY\"}
}" | jq -r '.runtime_id')
curl -sS -X POST "$API/agents/runtime/$RT/files/write" "${AUTH[@]}" \
-d '{"path": "/workspace/README.md", "content": "# demo\n\nA tiny sandbox project.\n"}' | jq .
# Wait until status is running, then (timeout is milliseconds):
curl -sS -X POST "$API/agents/runtime/$RT/commands/run" "${AUTH[@]}" \
-d '{
"command": "claude",
"args": [
"--dangerously-skip-permissions",
"--output-format", "stream-json",
"-p", "List files under /workspace and summarize README.md in one sentence."
],
"working_dir": "/workspace",
"timeout": 900000
}' | jq .
curl -sS -X DELETE "$API/agents/runtime/$RT" \
-H "Authorization: Bearer $GRAVIXLAYER_API_KEY"
When to use which format
| Goal | Flags |
|---|---|
| Human-readable CI log | --output-format text |
Parse session_id / structured result | --output-format json |
| Live progress in your UI | --output-format stream-json + on_stdout / --stream |