Run commands

You can run commands on a Computer directly, without an Agent Session: to set up a repository before starting work, inspect an Agent’s files, or run a job. This guide creates or finds a Computer, runs a command and reads its output. For what a Computer is, see Computers.

The examples use a Template definition with the ID repo deployed to the demo Project’s staging Environment, with the CLI configured (see Quickstart).

Create a Computer

helmr computer create repo --project demo --env staging \
  --key repo:acme/app \
  --idempotency-key computer:acme/app

The command prints the new Computer’s ID. repo is the ID from template({ id: "repo" }) in the current Deployment. --key is an optional, immutable name you can look the Computer up by later. --idempotency-key makes retries safe: repeating the command with the same key returns the same Computer. To bind Secrets at creation, pass --secrets-file; see Manage secrets.

Or use an Agent’s Computer

A Session started without --computer gets its own Computer. Find its ID with:

helmr session get SESSION_ID --project demo --env staging

The output includes computer_id.

Find a Computer

helmr computer get --key repo:acme/app --project demo --env staging
helmr computer get --id COMPUTER_ID --project demo --env staging --json

Pass exactly one of --id or --key. The JSON form includes status (available, deleting, deleted), residency (for example running or cold when hibernated) and, for an unavailable Computer, an error.

Run a command

helmr computer exec --key repo:acme/app --project demo --env staging \
  -- git -C /workspace/app status --short

Everything after -- is the command and its arguments; it runs without a shell. The command prints the command ID once Helmr has accepted the command, which can be before a hibernated Computer has resumed. Each run admits a new command; pass the same --idempotency-key to retry one admission after a lost response.

Flag Meaning
--cwd PATH Working directory. Defaults to /workspace.
--set-env NAME=VALUE Add an environment variable. Repeatable.
--stdin PATH Send a file’s bytes on standard input.
--timeout DURATION Execution limit. Defaults to 5m, at most 24h.

Commands can change the Computer’s disk; changes persist for later commands and Sessions on the same Computer. Do not pass Secret values in arguments or --set-env; bind them to the Computer instead.

Commands are bounded executions. When the main process exits, Helmr terminates remaining descendants, including children started with &. To run a process within the execution limit while your client does other work, start it as the command’s main process and keep the returned command ID. There is no background option; the command ID is returned immediately. Each command has a private process scope, so use its ID to observe or cancel it rather than relying on a PID from another command.

Wait for the outcome and read output

helmr command wait COMMAND_ID --timeout 10m --project demo --env staging
helmr command logs COMMAND_ID --follow --project demo --env staging

command wait prints status: settled and the outcome kind (exited, cancelled, timed_out or system_failed), or status: timeout if the wait ended first. A timeout ends only the wait. Use helmr command get COMMAND_ID --json for the exit code.

command logs writes the command’s stdout and stderr to the matching streams. Without --follow it reads one page and prints output_state (open, closed or unavailable) and a next_cursor to pass as --cursor. With --follow it keeps reading until the output closes, and fails if the complete output cannot be established. Missing ranges are reported as gaps, not skipped silently.

To stop a command, run helmr command cancel COMMAND_ID. The receipt confirms the request; read the outcome with command get or command wait.

Do the same with the SDK

import { HelmrClient } from "@helmr/sdk"

const client = new HelmrClient({
  url: process.env.HELMR_API_URL!,
  apiKey: process.env.HELMR_API_KEY!,
})

const computer = await client.computers.create({
  template: "repo",
  key: "repo:acme/app",
  idempotencyKey: "computer:acme/app",
})

const command = await computer.exec({
  command: ["git", "-C", "/workspace/app", "status", "--short"],
  timeout: "5m",
})

for await (const record of command.logs.stream()) {
  if (record.kind === "output") {
    const out = record.stream === "stdout" ? process.stdout : process.stderr
    out.write(record.content)
  } else {
    console.warn(`Missing ${record.stream} output: ${record.fromSequence}-${record.throughSequence}`)
  }
}

const outcome = await command.wait()
if (outcome.kind === "exited") console.log("exit code", outcome.exitCode)

To reach an existing Computer, use client.computers.ref(id), or find it by key with client.computers.list({ key }). Reconnect to a command with client.commands.ref(commandId). logs.stream yields byte chunks, not lines; store a record’s cursor and pass it as logs.stream({ after }) to resume. See the Computer reference.

Delete the Computer

helmr computer delete --key repo:acme/app --project demo --env staging

A Computer is also deleted automatically after it has been unused for its retention period, seven days unless the definition sets deleteAfterUnused.