Executions
An execution runs a Linux command on a Dedalus Machine, such as installing a package, running a script, or building an app. Start executions from the CLI or an SDK.
Executions are asynchronous: creating one returns an execution_id before the command finishes. Use that ID to check its status, read its output, or cancel it. If the machine is sleeping, it wakes before the command starts.
For an interactive terminal where you type commands as you work, use SSH.
Run a Linux command
The examples below require an existing machine. Use its machine_id wherever a machine ID is shown. To create one, see Create a machine.
The following example runs echo "hello world" on the remote machine. Commands follow the argument-vector convention described in execve(2): the first item names the executable, and each remaining item is one argument. The CLI accepts a JSON array. SDKs use a list or array in your language.
Arguments are passed directly to the executable. To use shell features such as &&, pipes, or variable expansion, invoke a shell explicitly. For example, ["sh", "-c", "echo hello world && ls -la"] runs both commands on the machine.
dedalus machines executions create \
--machine-id "$MACHINE_ID" \
--command '["echo", "hello world"]'
execution = client.machines.executions.create(
machine_id=machine_id,
command=["echo", "hello world"],
)
print(execution.execution_id)
const execution = await client.machines.executions.create({
machine_id: machineId,
command: ["echo", "hello world"],
});
console.log(execution.execution_id);
execution, err := client.Machines.Executions.New(ctx, dedalus.MachineExecutionNewParams{
MachineID: machineID,
ExecutionCreateParams: dedalus.ExecutionCreateParams{
Command: []string{"echo", "hello world"},
},
})
if err != nil {
return err
}
fmt.Println(execution.ExecutionID)
Save the returned execution_id and use it in the following requests. Assign it to EXECUTION_ID in the CLI, execution_id in Python, or executionId in TypeScript and Go.
Receiving an ID means the execution was created. Check its status to find out whether the command succeeded.
Check whether the command finished
Retrieve the execution using the ID returned when you created it. Repeat this request until the status is succeeded, failed, cancelled, or expired. These are terminal states, meaning the execution has finished.
When retry_after_ms is present, wait at least that many milliseconds before checking again.
dedalus machines executions retrieve \
--machine-id "$MACHINE_ID" \
--execution-id "$EXECUTION_ID"
execution = client.machines.executions.retrieve(
machine_id=machine_id,
execution_id=execution_id,
)
print(execution.status, execution.exit_code)
const execution = await client.machines.executions.retrieve({
machine_id: machineId,
execution_id: executionId,
});
console.log(execution.status, execution.exit_code);
execution, err := client.Machines.Executions.Get(ctx, dedalus.MachineExecutionGetParams{
MachineID: machineID,
ExecutionID: executionID,
})
if err != nil {
return err
}
fmt.Println(execution.Status, execution.ExitCode)
Active states are wake_in_progress, queued, and running. Terminal states are succeeded, failed, cancelled, and expired.
Read output
Once the command finishes, read its standard output (stdout) and standard error (stderr). For the echo example, stdout contains hello world. If the command failed, inspect its exit code, error details, and stderr.
The output response also includes total byte counts and truncation flags. Each inline stream is capped at 64 KiB.
dedalus machines executions output \
--machine-id "$MACHINE_ID" \
--execution-id "$EXECUTION_ID"
output = client.machines.executions.output(
machine_id=machine_id,
execution_id=execution_id,
)
print(output.stdout)
print(output.stderr)
const output = await client.machines.executions.output({
machine_id: machineId,
execution_id: executionId,
});
console.log(output.stdout);
console.error(output.stderr);
output, err := client.Machines.Executions.Output(ctx, dedalus.MachineExecutionOutputParams{
MachineID: machineID,
ExecutionID: executionID,
})
if err != nil {
return err
}
fmt.Print(output.Stdout)
fmt.Fprint(os.Stderr, output.Stderr)
Execution options
Use these options when creating an execution from the CLI or an SDK.
| Option | Purpose |
|---|---|
| Working directory | Choose where the command runs. Defaults to /root. |
| Environment variables | Pass configuration to the command. |
| Standard input | Supply UTF-8 text for the command to read when it starts. |
| Timeout | Limit how long the command runs. Specify a positive duration in milliseconds. |
The following example passes text to cat, which copies standard input to standard output. It sets the working directory, an environment variable, and a one-minute timeout.
dedalus machines executions create \
--machine-id "$MACHINE_ID" \
--command '["cat"]' \
--cwd /root \
--env '{"MODE":"test"}' \
--stdin 'hello world' \
--timeout-ms 60000
execution = client.machines.executions.create(
machine_id=machine_id,
command=["cat"],
cwd="/root",
env={"MODE": "test"},
stdin="hello world",
timeout_ms=60000,
)
print(execution.execution_id)
const execution = await client.machines.executions.create({
machine_id: machineId,
command: ["cat"],
cwd: "/root",
env: { MODE: "test" },
stdin: "hello world",
timeout_ms: 60000,
});
console.log(execution.execution_id);
execution, err := client.Machines.Executions.New(ctx, dedalus.MachineExecutionNewParams{
MachineID: machineID,
ExecutionCreateParams: dedalus.ExecutionCreateParams{
Command: []string{"cat"},
Cwd: dedalus.String("/root"),
Env: map[string]string{"MODE": "test"},
Stdin: dedalus.String("hello world"),
TimeoutMs: dedalus.Int(60000),
},
})
if err != nil {
return err
}
fmt.Println(execution.ExecutionID)
See the CLI guide for CLI setup and usage. Run dedalus machines executions create --help for the available flags, or use the SDK documentation for your language.
Read execution events
Events provide an ordered log of lifecycle changes and stdout or stderr chunks. Results are cursor-paginated.
dedalus machines executions events \
--machine-id "$MACHINE_ID" \
--execution-id "$EXECUTION_ID"
events = client.machines.executions.events(
machine_id=machine_id,
execution_id=execution_id,
)
for event in events:
print(event.sequence, event.type, event.chunk)
const events = client.machines.executions.events({
machine_id: machineId,
execution_id: executionId,
});
for await (const event of events) {
console.log(event.sequence, event.type, event.chunk);
}
page, err := client.Machines.Executions.Events(ctx, dedalus.MachineExecutionEventsParams{
MachineID: machineID,
ExecutionID: executionID,
})
if err != nil {
return err
}
for _, event := range page.Items {
fmt.Println(event.Sequence, event.Type, event.Chunk)
}
List executions
List executions on a machine, newest first. Results are cursor-paginated.
dedalus machines executions list --machine-id "$MACHINE_ID"
executions = client.machines.executions.list(machine_id=machine_id)
for execution in executions:
print(execution.execution_id, execution.status)
const executions = client.machines.executions.list({ machine_id: machineId });
for await (const execution of executions) {
console.log(execution.execution_id, execution.status);
}
page, err := client.Machines.Executions.List(ctx, dedalus.MachineExecutionListParams{
MachineID: machineID,
})
if err != nil {
return err
}
for _, execution := range page.Items {
fmt.Println(execution.ExecutionID, execution.Status)
}
Pass cursor from a prior response's next_cursor to fetch the next page. Use limit to set the page size.
Cancel an execution
Delete an execution to cancel it. Deleting an execution that has already reached a terminal state is a no-op.
dedalus machines executions delete \
--machine-id "$MACHINE_ID" \
--execution-id "$EXECUTION_ID"
client.machines.executions.delete(
machine_id=machine_id,
execution_id=execution_id,
)
await client.machines.executions.delete({
machine_id: machineId,
execution_id: executionId,
});
_, err := client.Machines.Executions.Delete(ctx, dedalus.MachineExecutionDeleteParams{
MachineID: machineID,
ExecutionID: executionID,
})
if err != nil {
return err
}
