nodes

Run a command (sync, or detach:true)

Run a shell command on a node. Synchronous (default): returns {exit_code, stdout, stderr, truncated}, budgeted by timeout (default and ceiling 60s here; nothing streams mid-call, and the HTTP door honors up to 300s). At the deadline the command is killed and the error carries the captured tail - only as good as your flushing (stdout is block-buffered off-tty: python -u / PYTHONUNBUFFERED=1); anything longer belongs in detach:true, whose log survives. Detached: returns {command_id, log_path} immediately; poll get_command for output and the exit code, kill_command to stop it. restart:"on-failure" respawns a detached command on nonzero exit (backoff, same log, then crash_loop; the node bills throughout). Write your verdict as JSON to $GMN_RESULT_PATH (injected) and it comes back on the status call. SECRETS GO IN env, NEVER THE COMMAND STRING: command lines are recorded (unless your org's retention policy declines it), env values ride only to the process (never recorded, names-only echo, redacted from logs); use short-lived, minimally scoped tokens and treat them as burned. Write files with the write_file tool. The tmux contract: files under ~ survive stop_node and wake; processes don't - a detached command dies with the container (it ends lost, no exit_code, no result; its log survives) - let a needed verdict reach an exit_code on get_command first. Push results out from inside the node before stopping. Calling this on a stopped node wakes it (billing starts when it's ready): you may get status "queued" (poll get_node, then re-call) or "waking" (re-call shortly); on_wake: true (detached, with max_duration_minutes) instead registers it to start the moment the node is ready. NEVER a sleep keepalive: "keep this node awake across turns" is hold_node - same node time, no command slot, no fake work on your mission page. The work lands on the node's mission automatically; pass mission: "<name>" only to attribute it elsewhere (a node reused under a new investigation).

post/preview/nodes/{name}/commands

Request

  • Base URL: https://autoresearch.sfcompute.com
  • URL: https://autoresearch.sfcompute.com/preview/nodes/{name}/commands
  • Auth: HTTP bearer

Path parameters

namestring required

Node name (from list_nodes).

Query parameters

workspacestring

Act in this workspace (its slug). Omitted: the token's bound workspace, then the organization's default. Never ambient - there is no switch-workspace mutation on this door.

Request body

commandstring required

The shell command, run by sh -c in the node's container as the dev user. Command lines are RECORDED (encrypted at rest, operator-visible as the debugging record): never put secrets here; pass them as env. Write files with write_file, never by decoding base64 on the command line: that records the bytes and has no integrity check, while write_file streams them off the command line and hashes the file on the node.

detachboolean nullable

true = detached: returns {command_id, log_path} immediately and the command keeps running on the node (poll get_command; kill_command stops it). Anything longer than the timeout ceiling belongs here.

envobject

Environment variables for the command (name → value), THE way to hand the node a secret (an HF token, a presigned URL). Values ride only to the process: never recorded, never echoed back (names only from get_command/list_commands), redacted from logs. Use short-lived, minimally scoped tokens and treat them as burned after the task. A value may also reference a stored org secret by name - {"secret": "wandb-prod"} (list_secrets) - resolved at launch so the value never appears in this chat; the process still sees the plain value, so burned-class care still applies. Vended workspace tokens (per-tenant credentials) cannot reference org secrets.

hold_on_failure_minutesinteger nullable

Minutes to hold the node awake if this DETACHED command FAILS - a nonzero exit, a signal death, or a restart budget exhausting into crash_loop. The moment a run fails is the moment its inputs are most valuable, because the retry needs exactly what the failed attempt had on disk, and by default a failure starts the idle timer like any other ending: the node stops and /scratch is destroyed while you are asleep. This keeps it alive so you can look. A clean exit is unaffected, and so is a command whose first launch never started (exit 127 with launch_error and no restarts). The node bills for the hold; ceilinged by node_hold_ceiling_hours, and release_node ends it early.

max_duration_minutesinteger nullable

REQUIRED with on_wake: how long the boot command may run before it is killed. A boot command whose node arrived at 3am and wedged bills until someone notices, so the bound is not optional and there is no default that would pick a number for you. Ceiling: boot_command_max_duration_ceiling_minutes (list_limits). At expiry the command is killed and the node returns to its normal grace window.

max_restartsinteger nullable

Restart budget for restart:"on-failure" (default 20). When it exhausts, the command ends as crash_loop - bounded burn by design.

max_waitstring nullable

How long a wake this command triggers may hold its place in the queue when capacity is busy (duration like "30m"; default 1h; ceiling 12h). The call itself still returns by timeout - the hold outlives it, and get_node polls the queue. Size this to the wait, not to your patience: the queued answer carries estimated_ready_seconds, and if the default hold is shorter than that estimate the response says so in hold_note - a lapsed hold means the node never arrives at all. Re-calling create_node with the node's name and a max_wait extends a queued hold (forward-only).

missionstring nullable

Attribute this command's work (and the node time it opens) to a mission (from open_mission): the investigation-level grouping whose page rolls up cost, results, and artifacts, and whose Commands tab shows this command line to your org (one more reason secrets ride env, never the command string). Node time reaches a mission's receipt through the commands that carry mission:, not through create_node. Omit and this work joins the node's own mission, which is usually what you want - pass it only to attribute the work somewhere else, as when you reuse a node for a new investigation.

on_wakeboolean nullable

true = register this as the node's BOOT COMMAND instead of running it now: the node must be stopped or queued, and the command starts the instant the node reaches ready, before the idle-grace clock can matter. The command_id comes back immediately and get_command answers for it (state "pending_wake") from that moment, so your next tick reads a command row rather than re-running a race. Requires max_duration_minutes. One per node: registering another replaces it and the response says which. Refused on a running node - drop on_wake to run the command now. This is how an agent that is not awake at the moment a node arrives still gets work started on it.

restartstring nullable

Restart policy for a DETACHED command: "never" (default) or "on-failure" - the worker respawns the process when it exits nonzero (exponential backoff, same log with a marker line per attempt, restart count on get_command). Exit 0 ends the command; kill_command always ends it. Bounded by max_restarts: when the budget exhausts the command ends as crash_loop and the node idles into grace as usual. The node keeps billing the whole time.

timeoutinteger nullable

Whole-call budget in seconds (default 60; ceiling 60 on this MCP connection - your client enforces its own per-call deadline, commonly 60s, so a longer budget could never be delivered here; the HTTP API door honors up to 300). Covers the wake when the node is stopped, then the command; at the deadline the command is killed. With detach:true it budgets only the wake; the command itself runs unbounded.

Response

The capability's JSON result.

objectstring

The resource type, when the result is one resource.

Changes

No recorded changes to this operation across all 1 revision of this API.