Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ab1a94a
initial Dockerfile, should install most of what we need to run the ru…
ellyxir Oct 3, 2025
70896cd
dockerfile now has container run indefinitely with sleep infinity com…
ellyxir Oct 6, 2025
8896dd4
Add instructions on how to run the docker container locally
ellyxir Oct 6, 2025
916f986
added claude
ellyxir Oct 6, 2025
bb9fe3d
added instructions on how to remove images
ellyxir Oct 6, 2025
e2dc616
added codex cli
ellyxir Oct 6, 2025
81f59b6
git clone labs repo, start deno for shell and toolshed
ellyxir Oct 6, 2025
184c547
created ralph user, added to sudoers
ellyxir Oct 6, 2025
1a9cc97
added extra information on how to deploy charms and append to AGENTS.…
ellyxir Oct 6, 2025
3dd6e01
added extra information on how to deploy charms and append to AGENTS.…
ellyxir Oct 6, 2025
5b7a5e5
Merge branch 'main' into ellyse/docker-container-ralph
ellyxir Oct 6, 2025
c33320c
fix: Docker build and runtime improvements
ellyxir Oct 6, 2025
18dc936
added information on how to use prebuilt docker image
ellyxir Oct 6, 2025
df938bd
moved ralph out of packages into ./tools/ralph
ellyxir Oct 6, 2025
5d9458e
moved npm packages for claude and codex into user space
ellyxir Oct 7, 2025
9188bd3
Update base image tools/ralph/Dockerfile
ellyxir Oct 7, 2025
4f5146c
switched to npm because newer python doesnt like pip conflicting with…
ellyxir Oct 7, 2025
8bf8c53
remove tailscale, dont think we need it
ellyxir Oct 7, 2025
bf47160
pin dockerfile for deno version
ellyxir Oct 7, 2025
c016641
consolidated apt-get and updates
ellyxir Oct 7, 2025
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
91 changes: 91 additions & 0 deletions tools/ralph/DEPLOY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Deploying and Testing a Charm

This guide provides step-by-step instructions for deploying a charm locally and
testing it with Playwright.

## Step 1: Create an Identity Key

First, create an identity key if one doesn't exist:

```bash
NO_COLOR=1 deno task ct id new > my.key
```

**Important:** Do NOT add `2>&1` to this command - it will corrupt the key file
by mixing error output with the actual key.

This creates a new identity key file named `my.key` in the current directory.

## Step 2: Deploy the Charm

Deploy a charm to localhost using the `ct charm new` command:

```bash
deno task ct charm new --identity ./my.key --api-url http://127.0.0.1:8000 --space <SPACE_NAME> <PATH_TO_CHARM_FILE>
```

Example:

```bash
deno task ct charm new --identity ./my.key --api-url http://127.0.0.1:8000 --space ellyse ./packages/patterns/counter.tsx
```

The command will output a charm ID (e.g.,
`baedreidon464mghox4uar46bbym5t6bnmlvn6wwzby5vvdmsw24oxaalp4`).

## Step 3: Construct the URL

The URL format for localhost is:

```
http://localhost:5173/<SPACE_NAME>/<CHARM_ID>
```

Example:

```
http://localhost:5173/ellyse/baedreidon464mghox4uar46bbym5t6bnmlvn6wwzby5vvdmsw24oxaalp4
```

## Step 4: Test with Playwright

### 4.1 Navigate to the Charm URL

```javascript
await page.goto("http://localhost:5173/<SPACE_NAME>/<CHARM_ID>");
```

### 4.2 Register/Login (First Time Only)

When you first visit, you'll see a login page. Register with a passphrase:

1. Click the "➕ Register" button
2. Click the "🔑 Generate Passphrase" button
3. Click the "🔒 I've Saved It - Continue" button

This will log you in and load the charm.

### 4.3 Test the Charm

Once logged in, you can interact with the charm using Playwright commands.

## Complete Example

```bash
# 1. Create identity key (if needed)
deno task ct id new > my.key

# 2. Deploy charm
deno task ct charm new --identity ./my.key --api-url http://127.0.0.1:8000 --space ellyse ./packages/patterns/counter.tsx

# Output: baedreidon464mghox4uar46bbym5t6bnmlvn6wwzby5vvdmsw24oxaalp4

# 3. URL will be:
# http://localhost:5173/ellyse/baedreidon464mghox4uar46bbym5t6bnmlvn6wwzby5vvdmsw24oxaalp4
```

Then use Playwright to:

1. Navigate to the URL
2. Complete registration (first time)
3. Test the charm functionality
84 changes: 84 additions & 0 deletions tools/ralph/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

# Base tools
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
wget \
git \
ripgrep \
build-essential \
ca-certificates \
gnupg \
lsb-release \
unzip \
sudo \
software-properties-common

# GitHub CLI
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | \
gpg --dearmor -o /usr/share/keyrings/githubcli-archive-keyring.gpg && \
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | \
tee /etc/apt/sources.list.d/github-cli.list > /dev/null && \
apt-get update && apt-get install -y gh

# Node.js 24 (via Nodesource)
RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && \
apt-get install -y nodejs

# Deno (install globally to /usr/local with pinned version)
RUN curl -fsSL https://deno.land/install.sh | DENO_INSTALL=/usr/local sh -s v2.5.2

# Helix editor (requires software-properties-common from base tools)
RUN add-apt-repository -y ppa:maveonair/helix-editor && \
apt-get update && \
apt-get install -y helix

# MyST Markdown (via npm to avoid Python PEP 668 issues)
RUN npm install -g mystmd

# Install Playwright browser and dependencies
RUN npx playwright install chrome && \
npx playwright install-deps chrome

# Create ralph user (with sudo privileges for development)
RUN useradd -m -s /bin/bash ralph && \
echo "ralph ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

# Set up working directory with proper ownership
RUN mkdir -p /app && chown ralph:ralph /app
WORKDIR /app

# Clone the Common Tools repository into labs subdirectory
RUN git clone https://github.com/commontoolsinc/labs.git /app/labs && \
chown -R ralph:ralph /app/labs

# Copy and append DEPLOY.md to AGENTS.md
COPY --chown=ralph:ralph DEPLOY.md /tmp/DEPLOY.md
RUN cat /tmp/DEPLOY.md >> /app/labs/AGENTS.md && \
rm /tmp/DEPLOY.md

# Copy the startup script from local directory
COPY --chown=ralph:ralph start-servers.sh /app/start-servers.sh
RUN chmod +x /app/start-servers.sh

# Switch to ralph user
USER ralph

# Configure npm to install global packages in user directory
RUN npm config set prefix ~/.npm-global

# Add npm-global bin to PATH
ENV PATH="/home/ralph/.npm-global/bin:$PATH"

# Install Claude CLI and Codex as ralph user (can auto-update)
RUN npm install -g @anthropic-ai/claude-code && \
npm install -g @openai/codex

# Configure Claude MCP server for ralph user
# --no-sandbox is required because Docker containers restrict namespace creation
RUN claude mcp add --scope user playwright npx "@playwright/mcp@latest" -- --headless --isolated --no-sandbox
Comment on lines +79 to +81
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a similar approach for registering an mcp server with codex?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, but exactly the same but in the README.md i have a TODO item to do just that, in a followup PR


# Start Common Tool servers
CMD ["/app/start-servers.sh"]
95 changes: 95 additions & 0 deletions tools/ralph/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# README

Docker container to run the Common Tools servers

Ability to run [Ralph](https://ghuntley.com/ralph/)

Claude CLI and Codex are installed

## How to run Ralph

### Using pre-built image from Docker Hub (recommended)

```bash
$ docker pull ellyxir/ralph
$ docker run -d --name ralph -p 8000:8000 -p 5173:5173 ellyxir/ralph
```

To connect to the container (connecting as ralph user is recommended):

```bash
$ docker exec -it -u ralph ralph bash # Connect as ralph user (recommended)
# OR
$ docker exec -it ralph bash # Connect as root (if needed for admin tasks)
```

### Building locally

If you want to build the image yourself with local modifications:

```bash
$ cd ./tools/ralph
$ docker build -t <user_name>/ralph .
$ docker run -d --name ralph -p 8000:8000 -p 5173:5173 <user_name>/ralph
```

Note for `docker build`:

- -t is for the tag, we use _ralph_ here

Note for `docker run`:

- -d is for detached mode
- --name gives it an easier name to use for connecting to it later
- the last _<user_name>/ralph_ referes to the build tag we used earlier

Connecting to the running container:

```bash
$ docker exec -it -u ralph ralph bash # Connect as ralph user (recommended)
$ docker exec -it ralph bash
```

We are using the `--name ralph` we specified earlier to connect.

Running Claude Code with all permissions:

```bash
$ claude --dangerously-skip-permissions
```

## Removing ralph

You must remove the existing version if you want to run a newer build:

```bash
$ docker stop ralph
$ docker rm ralph
```

## Pushing new image to Dockerhub

```
$ docker login
$ docker push <user_name>/ralph
```

## TODO

- add playwright to codex
- push a working image to a docker hub
- update README to use image from dockerhub
- figure out how LLM tokens should be set for toolshed
- sandbox the container (network config)
- make ralph easy to run
- DONE - change permissions so claude auto updater will work
- DONE - move ralph script into ./tools/ralph
- DONE - Add codex and claude packages
- DONE - write section how to run ralph in this file
- DONE - git clone the common tools repositories
- DONE - start up toolshed server
- DONE - start up shell server
- DONE - add playwright mcp to claude
- created ralph user since chrome doesnt like to run as root, probably better
this way anyway
- made ralph sudoer
38 changes: 38 additions & 0 deletions tools/ralph/start-servers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash

# Start toolshed server in background
echo "Starting toolshed server..."
cd /app/labs/packages/toolshed && deno task dev &
TOOLSHED_PID=$!

# Start shell server in background
echo "Starting shell server..."
cd /app/labs/packages/shell && deno task dev-local &
SHELL_PID=$!

# Function to handle shutdown
cleanup() {
echo "Shutting down servers..."
kill $TOOLSHED_PID $SHELL_PID 2>/dev/null
exit 0
}

# Set up signal handlers
trap cleanup SIGTERM SIGINT

# Keep the script running and show logs
echo "Servers started. Press Ctrl+C to stop."
echo "Toolshed PID: $TOOLSHED_PID"
echo "Shell PID: $SHELL_PID"

# Wait for both processes and capture their exit statuses
wait $TOOLSHED_PID
TOOLSHED_EXIT=$?
wait $SHELL_PID
SHELL_EXIT=$?

# Exit with error if either server failed
if [ $TOOLSHED_EXIT -ne 0 ] || [ $SHELL_EXIT -ne 0 ]; then
echo "Server exited with error (toolshed: $TOOLSHED_EXIT, shell: $SHELL_EXIT)"
exit 1
fi