Skip to content
Open
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
209 changes: 209 additions & 0 deletions articles/20260822_run_omni_and_claude_engineer_in_daytona.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
---
title: 'Run Omni and Claude Engineer Inside a Daytona Workspace'
description:
'A step-by-step guide to running Doriandarko AI engineer consoles in
Daytoona workspaces using devcontainers, from setup to a working session.'
date: 2026-08-22
author: 'Esteban Aulestia'
tags: ['ai-engineer', 'devcontainer', 'openrouter', 'anthropic', 'tutorial']
---

# Run Omni and Claude Engineer Inside a Daytona Workspace

_[AI engineering assistants have moved quickly from novelty to daily
instrument. Two popular open-source examples are
[Omni Engineer](https://github.com/Doriandarko/omni-engineer) and its
predecessor [Claude Engineer](https://github.com/Doriandarko/claude-engineer):
console-based tools that bring file editing, web search, image analysis and
multi-file diffing into a terminal conversation with an LLM. They are powerful,
but they share a classic friction problem: every machine you want to use them
on needs a matching Python environment, the right system libraries, API keys in
the right place, and a shell configured the way the tools expect.]_

_[This is precisely the problem [Daytona](https://www.daytona.io) was built to
eliminate. A Daytona workspace gives any repository a reproducible, sandboxed
development environment that starts in seconds and can be thrown away without
remorse. In this guide we will wire both engineer projects up with standard
[Development Containers](https://containers.dev) configuration, contribute
those files back to the upstream repositories, and then run real working
sessions inside Daytona workspaces — first with Omni Engineer on OpenRouter,
then with Claude Engineer against the Anthropic API.]_

## TL;DR

- **Devcontainers make the engineers portable**: _[a single
`.devcontainer/devcontainer.json` per repository pins Python 3.11,
installs dependencies automatically and forwards API keys safely.]_
- **Daytona runs them anywhere**: _[`daytona create` turns each repo into a
ready-to-use workspace; no local Python, venv or PortAudio juggling.]_
- **Two full walkthroughs**: _[one console session with Omni Engineer solving
a refactoring task, and one Flask-backed session with Claude Engineer.]_
- **Everything is reproducible**: _[the devcontainer files were contributed
upstream, so anyone can repeat this guide with two commands.]_

## Why Devcontainers + Daytona for AI Consoles

_[Tools like Omni Engineer depend on a surprisingly long tail of
requirements: `rich` and `pygments` for terminal rendering, `duckduckgo-search`
for web lookups, `Pillow` for image processing, `prompt_toolkit` for input.
Claude Engineer adds Flask, matplotlib and optional E2B sandbox execution. On a
personal laptop these installs collide with other projects constantly. Worse,
when a demo breaks mid-session because of a dependency conflict, debugging the
environment eats exactly the time the assistant was supposed to save.]_

_[A devcontainer declares the entire environment as code: base image, system
packages, post-create steps and editor extensions. Daytona reads that same
declaration when creating a workspace, so "it works on my machine" becomes "it
works identically on every machine". Because workspaces are isolated, you can
run both engineers side by side — or delete and recreate them in seconds when
an experiment goes sideways.]_

**Key Point:** _[The devcontainer files added by this guide live inside the
project repositories themselves, so no external template registry is needed.]_

## Step 1 — Add a Devcontainer to Omni Engineer

_[Omni Engineer is a console application driven entirely by one entry point,
`main.py`, and a short `requirements.txt`. Its only mandatory secret is an
OpenRouter API key exposed through `python-dotenv`. The following
`.devcontainer/devcontainer.json` captures all of it:]_

```json
{
"name": "Omni Engineer",
"image": "mcr.microsoft.com/devcontainers/python:3.11",
"postCreateCommand": "pip install -r requirements.txt",
"containerEnv": {
"OPENROUTER_API_KEY": "${localEnv:OPENROUTER_API_KEY}"
},
"customizations": {
"vscode": {
"extensions": ["ms-python.python", "ms-python.vscode-pylance"]
}
},
"remoteUser": "vscode"
}
```

_[Three details matter here. First, `postCreateCommand` installs the exact
dependency set from `requirements.txt` every time the container is built, so
drift between contributors is impossible. Second, `containerEnv` forwards your
local `OPENROUTER_API_KEY` into the container without ever writing it to disk
inside the workspace. Third, the non-root `vscode` user keeps file ownership
sane when you mount or commit from inside the workspace.]_

## Step 2 — Do the Same for Claude Engineer

_[Claude Engineer has a slightly larger surface: a Flask UI in addition to the
console, plus an optional E2B key for sandboxed code execution. Its
devcontainer mirrors the previous one but also forwards port `5000` so the web
interface is reachable from the browser:]_

```json
{
"name": "Claude Engineer",
"image": "mcr.microsoft.com/devcontainers/python:3.11",
"postCreateCommand": "pip install -r requirements.txt",
"containerEnv": {
"ANTHROPIC_API_KEY": "${localEnv:ANTHROPIC_API_KEY}",
"E2B_API_KEY": "${localEnv:E2B_API_KEY}"
},
"forwardPorts": [5000]
}
```

**Key Point:** _[Both files were contributed upstream through pull requests,
so after they merge, cloning either repository in Daytona picks the
configuration up automatically — zero manual setup.]_

## Step 3 — Create the Workspace in Daytona

_[With the devcontainer committed, spinning up the environment is a single
command:]_

```bash
daytona create https://github.com/Doriandarko/omni-engineer
```

_[Daytona detects `.devcontainer/devcontainer.json`, builds the container,
runs the post-create installation and drops you into an attached editor. On a
fresh machine this replaces roughly ten minutes of manual environment work
with about forty seconds of automated provisioning. Repeat the same command
against the `claude-engineer` repository for the second environment.]_

_[Inside the workspace, export your keys (or rely on the automatic forwarding
from your local shell) and start the console:]_

```bash
export OPENROUTER_API_KEY="sk-or-..."
python main.py
```

## Step 4 — A Real Working Session With Omni Engineer

_[To demonstrate the tool end to end, the session below tackles a small but
realistic task: adding retry handling to a script that calls a flaky HTTP
endpoint. Everything happens inside the Daytona workspace terminal.]_

1. **Add the target file to the AI context**:

```text
/add src/fetch_report.py
```

2. **Describe the change in plain language**:

```text
Wrap the requests.get call in fetch_report.py with exponential backoff.
Max 3 retries, respect Retry-After headers, log each attempt with rich.
```

3. **Review the proposed diff**: _[Omni Engineer returns a colored diff
instead of rewriting the file blindly. Accept it, and `/undo` remains
available if the result disappoints.]_

4. **Search the web without leaving the console**:

```text
/search urllib3 Retry Retry-After best practice
```

_[The entire flow — edit, diff, undo, lookup — happened in one terminal inside
one disposable workspace. When the task was done, the workspace kept running
for follow-up questions; when the experiment was over, `daytona delete`
removed every trace.]_

## Step 5 — Claude Engineer Behind Flask

_[Claude Engineer exposes a browser chat backed by the Anthropic API. After
creating its workspace the same way, launch the server:]_

```bash
export ANTHROPIC_API_KEY="sk-ant-..."
python app.py
```

_[Daytona forwards port `5000` automatically thanks to the `forwardPorts`
entry, so opening the preview URL shows the chat interface. Asking it to
"scaffold a pytest suite for fetch_report.py" produces files directly in the
workspace tree — the same files a human teammate would commit later.]_

## Troubleshooting

- **API key not visible inside the container**: _[make sure the variable is
exported in the local shell *before* running `daytona create`; the
`${localEnv:...}` interpolation happens at build time.]_
- **Port already in use for the Flask UI**: _[only applies outside Daytona;
inside the workspace each project owns its own network namespace.]_
- **DuckDuckGo rate limits**: _[web search in Omni Engineer is subject to
public rate limits; space out `/search` calls during long sessions.]_

## Conclusion

_[Reproducible environments are the quiet foundation of productive AI-assisted
development. By committing two small devcontainer definitions upstream and
letting Daytona consume them, both Omni Engineer and Claude Engineer went from
"install carefully, hope it works" to "two commands, always works" — on any
machine, for any contributor. The same pattern extends naturally to your own
internal tools: if a repository has a devcontainer, Daytona makes it a
workspace; if it does not, fifteen lines of JSON fix that permanently.]_
13 changes: 13 additions & 0 deletions authors/esteban_aulestia.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Author: Esteban Aulestia
Title: Software Engineer
Description: Software engineer focused on developer tooling and AI-assisted
workflows. Writes practical guides about reproducible development
environments, containers and open-source AI engineering assistants.
Author Image: https://avatars.githubusercontent.com/ESTEBANTRAN?size=512
Author LinkedIn:
Author Twitter:
Company Name: Independent
Company Description: Independent software development practice building and
contributing to open-source developer tools.
Company Logo Dark:
Company Logo White: