Compare commits
10 Commits
46b52b2207
...
feature/do
| Author | SHA1 | Date | |
|---|---|---|---|
| e6e5eadcf8 | |||
| 48caec8669 | |||
|
|
1f357fe2e3 | ||
|
|
4425dcfd41 | ||
|
|
539d2eb4d6 | ||
| 5675c5e75a | |||
|
|
1d45a7caea | ||
|
|
8a4e21fc2e | ||
| f466a99e69 | |||
|
|
113db0b0dd |
162
README.md
162
README.md
@@ -1,51 +1,153 @@
|
||||
# app-opencode-experiment
|
||||
# app-opencode
|
||||
|
||||
[opencode](https://opencode.ai/) in a Docker container, wired to [weave](https://weave.redpencil.io) as the model provider. Default model `weave/glm-5.2`. LSP baked in for TypeScript/JS and Python.
|
||||
[opencode](https://opencode.ai/) packaged in a Docker container and wired to
|
||||
[weave](https://weave.redpencil.io) as the model provider. It ships with an
|
||||
LSP for TypeScript/JavaScript and Python baked in, so opencode gets real
|
||||
type-aware navigation out of the box. The default model is `weave/glm-5.2`.
|
||||
|
||||
## Setup
|
||||
The docs below are split into three parts: a **Getting started** walkthrough to
|
||||
run it the first time, **How-to guides** for specific tasks, and a **Reference**
|
||||
for the moving parts.
|
||||
|
||||
Create a `docker-compose.override.yml` (gitignored) with your weave key and repo mount:
|
||||
> `drc` in the examples is shorthand for `docker compose`
|
||||
> (`alias drc='docker compose'`). Substitute `docker compose` if you don't have
|
||||
> the alias.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
opencode:
|
||||
environment:
|
||||
WEAVE_API_KEY: "..."
|
||||
volumes:
|
||||
- /path/to/your/repo:/workspace/repo
|
||||
```
|
||||
## Getting started
|
||||
|
||||
Then:
|
||||
A first run from a clean checkout.
|
||||
|
||||
```
|
||||
drc up -d
|
||||
drc exec opencode opencode
|
||||
```
|
||||
1. **Get a weave API key.** You need an account on
|
||||
[weave.redpencil.io](https://weave.redpencil.io/ui/) and/or an API key (ask us!).
|
||||
|
||||
## TUI
|
||||
2. **Create your local override.** `docker-compose.override.yml` is gitignored
|
||||
and holds your key plus the repo you want opencode to work on. Create it with
|
||||
your key and mount:
|
||||
|
||||
In the TUI, type `/models` to select a model from the registered weave models.
|
||||
```yaml
|
||||
services:
|
||||
opencode:
|
||||
environment:
|
||||
WEAVE_API_KEY: "your-key-here"
|
||||
volumes:
|
||||
- /path/to/your/repo:/workspace/repo
|
||||
```
|
||||
|
||||
## Layout
|
||||
3. **Start the container.**
|
||||
|
||||
- `config/opencode/` - image: Dockerfile + `opencode.json` (mounted live, see below)
|
||||
- `data/` - persisted opencode state (gitignored)
|
||||
- `docker-compose.yml` - opencode service
|
||||
```
|
||||
drc up -d
|
||||
```
|
||||
|
||||
## Updating the config
|
||||
4. **Open the TUI.**
|
||||
|
||||
`opencode.json` is mounted into the container, so edits take effect with a restart — no rebuild needed:
|
||||
```
|
||||
drc exec opencode opencode
|
||||
```
|
||||
|
||||
You're now in opencode, talking to `weave/glm-5.2`. Type `/models` to switch
|
||||
models, or just start prompting.
|
||||
|
||||
## How-to guides
|
||||
|
||||
### Switch the model in a session
|
||||
|
||||
In the TUI, type `/models` and pick from the registered weave models. This only
|
||||
changes the current session; to change the default for every session, edit
|
||||
`opencode.json` (see below).
|
||||
|
||||
### Change the default model
|
||||
|
||||
Edit `model` (and optionally `small_model`) in
|
||||
[`config/opencode/opencode.json`](config/opencode/opencode.json), then apply the
|
||||
change:
|
||||
|
||||
```
|
||||
drc restart opencode
|
||||
```
|
||||
|
||||
Rebuild only when changing the `Dockerfile` or installed packages:
|
||||
`opencode.json` is mounted into the container, so a restart is all that's
|
||||
needed.
|
||||
|
||||
```
|
||||
drc build opencode
|
||||
### Register a new weave model
|
||||
|
||||
Add the model under `provider.weave.models` in `opencode.json`:
|
||||
|
||||
```json
|
||||
"provider": {
|
||||
"weave": {
|
||||
"models": {
|
||||
"my-new-model": { "tools": true }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Caveats
|
||||
Then `drc restart opencode`. It will show up in the `/models` picker.
|
||||
See the [model list on weave](https://weave.redpencil.io/ui/models-and-endpoints/) for an overview of available models. Need another model, please let us know :)
|
||||
|
||||
- Copy from the TUI is weird over Docker: To copy text out of the opencode TUI running in a container, I have to do `Shift`+click to select the text, then `Ctrl`+`Shift`+`C` to copy. Plain `COPY` doesn't work.
|
||||
### Copy text out of the TUI
|
||||
|
||||
Copying from the opencode TUI over Docker is fiddly: `Shift`+click to select the
|
||||
text, then `Ctrl`+`Shift`+`C` to copy. A plain copy doesn't work.
|
||||
|
||||
### Connect opencode to a (backend) service
|
||||
|
||||
To let opencode call a service over HTTP, e.g. a stack you've brought up in dev
|
||||
mode and want the agent to follow up against, have that stack join opencode's
|
||||
network instead of publishing ports or going via the host.
|
||||
|
||||
opencode's stack creates a default network named `app-opencode_default`. Attach the
|
||||
dev-mode service to it as an external network:
|
||||
|
||||
```yaml
|
||||
# the dev-mode app stack
|
||||
services:
|
||||
app:
|
||||
networks:
|
||||
- default
|
||||
- app-opencode_default
|
||||
|
||||
networks:
|
||||
app-opencode_default:
|
||||
external: true
|
||||
```
|
||||
|
||||
opencode can then reach it at `http://app:<port>`, where `app` is the **service
|
||||
name** from that stack's compose and `<port>` is the container's *internal* port —
|
||||
no `ports:` publishing needed.
|
||||
|
||||
Note: Bring the opencode stack up first, so `app-opencode_default` exists before adding to your app.
|
||||
|
||||
### Executing docker commands from opencode
|
||||
The stack runs a dedicated dind (docker in docker) container, and opencode can drive it as a client (`DOCKER_HOST=tcp://dind:2375`). So from inside opencode you can build images, run containers, and `docker compose up` a project's stack. The agent can spin up and test the services it's working on.
|
||||
|
||||
A few things that differ from a normal host, because the daemon lives in dind:
|
||||
|
||||
- Containers and published ports you start run on the **dind** daemon. Reach a running service at `dind:<port>`, **not** `localhost:<port>`. opencode does not
|
||||
share dind's network namespace, so `localhost` won't reach the stack.
|
||||
- The repo needs to be mounted at the same absolute path (`/workspace/repo`) in both the opencode and dind containers, so compose bind-mounts resolve correctly. Work on the checkout there; edits elsewhere won't be visible to the containers dind runs.
|
||||
- The dind image store is cached in `./data/dind-cache`, so pulled images stay warm across restarts.
|
||||
- To access the services from the host machine, you will need to connect to the dind container or publish the necessary ports from the dind container to your host.
|
||||
|
||||
> Note: Opencode itself doesn't run in a privileged container so it can't do privileged actions directly, but it can use the docker daemon in dind to do the same things. Because dind is privileged and in the host user namespace, a container started inside dind can mount the host's raw disk devices and reach the **entire** host filesystem, not only what you shared. This needs opencode to actively do it, but it's a well-known technique, not a theoretical edge case.
|
||||
|
||||
## Reference
|
||||
|
||||
### `opencode.json` keys
|
||||
|
||||
- **`model` / `small_model`** — default model and the model used for lighter
|
||||
tasks. Both default to `weave/glm-5.2`.
|
||||
- **`provider.weave`** — the weave provider, using the
|
||||
`@ai-sdk/openai-compatible` adapter against `https://weave.redpencil.io/v1`
|
||||
with the key from `WEAVE_API_KEY`.
|
||||
- **`provider.weave.models`** — the models offered in `/models`.
|
||||
- **`lsp`** — language servers baked into the image:
|
||||
`typescript-language-server` for TS/JS and `pyright` for Python.
|
||||
- **`permission`** — set to `ask` for read, edit, bash, and external-directory
|
||||
actions, so opencode prompts before acting.
|
||||
|
||||
### Caveats
|
||||
|
||||
- Copy from the TUI over Docker needs the `Shift`+click / `Ctrl`+`Shift`+`C`
|
||||
dance described above.
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
FROM node:22-slim
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git curl ca-certificates python3 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN npm install -g \
|
||||
opencode-ai@1.17.9 \
|
||||
typescript \
|
||||
typescript-language-server \
|
||||
pyright
|
||||
|
||||
ENV NODE_PATH=/usr/local/lib/node_modules
|
||||
|
||||
COPY opencode.json /root/.config/opencode/opencode.json
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
CMD ["sleep", "infinity"]
|
||||
@@ -38,103 +38,22 @@
|
||||
"apiKey": "{env:WEAVE_API_KEY}"
|
||||
},
|
||||
"models": {
|
||||
"claude-sonnet": {
|
||||
"tools": true
|
||||
},
|
||||
"claude-opus": {
|
||||
"tools": true
|
||||
},
|
||||
"claude-haiku": {
|
||||
"tools": true
|
||||
},
|
||||
"Mistral-Small-3.2-24B-Instruct-2506": {
|
||||
"tools": true
|
||||
},
|
||||
"phi-4": {
|
||||
"tools": true
|
||||
},
|
||||
"gemini-3.5-flash": {
|
||||
"tools": true
|
||||
},
|
||||
"gemini-3.1-pro": {
|
||||
"tools": true
|
||||
},
|
||||
"qwen3.6-35B-A3B": {
|
||||
"tools": true
|
||||
},
|
||||
"qwen3-coder-480B-A35B-Instruct": {
|
||||
"kimi-K2.7-code": {
|
||||
"tools": true
|
||||
},
|
||||
"glm-5.2": {
|
||||
"tools": true
|
||||
},
|
||||
"kimi-K2.7-code": {
|
||||
"devstral": {
|
||||
"tools": true
|
||||
},
|
||||
"MiniMax-M2.7": {
|
||||
"tools": true
|
||||
},
|
||||
"DeepSeek-V4-Flash": {
|
||||
"tools": true
|
||||
},
|
||||
"DeepSeek-V4-Pro": {
|
||||
"tools": true
|
||||
},
|
||||
"nemotron-3-ultra": {
|
||||
"tools": true
|
||||
},
|
||||
"mimo-v2.5-pro": {
|
||||
"tools": true
|
||||
},
|
||||
"qwen3-max-thinking": {
|
||||
"tools": true
|
||||
},
|
||||
"gpt-oss-120b": {
|
||||
"tools": true
|
||||
},
|
||||
"mistral-large": {
|
||||
"local-qwen3.6:27b": {
|
||||
"tools": true
|
||||
},
|
||||
"mistral-medium": {
|
||||
"tools": true
|
||||
},
|
||||
"mistral-small": {
|
||||
"tools": true
|
||||
},
|
||||
"magistral-medium": {
|
||||
"tools": true
|
||||
},
|
||||
"magistral-small": {
|
||||
"tools": true
|
||||
},
|
||||
"codestral": {
|
||||
"tools": true
|
||||
},
|
||||
"devstral": {
|
||||
"tools": true
|
||||
},
|
||||
"mistral-embed": {
|
||||
"tools": false
|
||||
},
|
||||
"mistral-ocr": {
|
||||
"tools": false
|
||||
},
|
||||
"ministral-14b": {
|
||||
"tools": true
|
||||
},
|
||||
"ministral-8b": {
|
||||
"tools": true
|
||||
},
|
||||
"ministral-3b": {
|
||||
"tools": true
|
||||
},
|
||||
"local-gemma3:27b-it-qat": {
|
||||
"tools": true
|
||||
},
|
||||
"local-gpt-oss:20b": {
|
||||
"tools": true
|
||||
},
|
||||
"local-qwen3.6:27b": {
|
||||
"claude-sonnet": {
|
||||
"tools": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,30 @@
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
opencode:
|
||||
build: ./config/opencode/
|
||||
image: redpencil/opencode:feature-with-docker
|
||||
volumes:
|
||||
- ./data/opencode/share:/root/.local/share/opencode
|
||||
- ./config/opencode/opencode.json:/root/.config/opencode/opencode.json
|
||||
# - ./config/opencode:/root/.config/opencode
|
||||
depends_on:
|
||||
dind:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DOCKER_HOST: "tcp://dind:2375"
|
||||
|
||||
dind:
|
||||
image: docker:29.6-dind
|
||||
privileged: true
|
||||
# userns mode only required if you use user namespaces
|
||||
userns_mode: "host"
|
||||
environment:
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
volumes:
|
||||
- ./data/dind-cache:/var/lib/docker
|
||||
command:
|
||||
- "--registry-mirror=https://docker-registry-mirror.redpencil.io"
|
||||
healthcheck:
|
||||
test: ["CMD", "docker", "info"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 20
|
||||
start_period: 10s
|
||||
|
||||
Reference in New Issue
Block a user