Rhema had no way to spend a ChatGPT subscription: the models were reachable only through an OpenAI API key, which bills the API account instead. The new adapter signs in through OpenAI's browser OAuth flow with PKCE and a loopback callback, stores the returned token JSON as the instance credential, and sends turns to the Codex Responses backend with the subscription's live model catalog. The flow runs on the existing InteractiveLogin seam, so the wizard, the settings dialog, and the headless frontend all drive it. The TUI shows the link's host with an open button and a copy button: enter opens the default browser, and c copies the full URL, which keeps a long authorization query off the screen and out of a manual selection. The provider registry, config, ACP, claw, and the web settings page gain the new provider kind.
111 lines
6.1 KiB
Docker
111 lines
6.1 KiB
Docker
# rhema-claw: the resident forge-watching daemon.
|
|
# Build from the repo root (the context is the repo):
|
|
# docker build -f deploy/docker/claw.Dockerfile .
|
|
|
|
# The builder tag tracks rust-toolchain.toml: the pinned channel is what
|
|
# every commit is tested against. The full image (not -slim) carries the C
|
|
# toolchain rusqlite's bundled SQLite build needs.
|
|
FROM rust:1.98.1 AS builder
|
|
WORKDIR /build
|
|
|
|
# Manifests first: the registry cache this populates survives source edits.
|
|
COPY Cargo.toml Cargo.lock rust-toolchain.toml ./
|
|
COPY crates/mcp-to-cli/Cargo.toml crates/mcp-to-cli/Cargo.toml
|
|
COPY crates/rhema-acp/Cargo.toml crates/rhema-acp/Cargo.toml
|
|
COPY crates/rhema-agent-tools/Cargo.toml crates/rhema-agent-tools/Cargo.toml
|
|
COPY crates/rhema-autonomous-agent/Cargo.toml crates/rhema-autonomous-agent/Cargo.toml
|
|
COPY crates/rhema-claw/Cargo.toml crates/rhema-claw/Cargo.toml
|
|
COPY crates/rhema-cloud-agent/Cargo.toml crates/rhema-cloud-agent/Cargo.toml
|
|
COPY crates/rhema-cloudflare-code/Cargo.toml crates/rhema-cloudflare-code/Cargo.toml
|
|
COPY crates/rhema-code/Cargo.toml crates/rhema-code/Cargo.toml
|
|
COPY crates/rhema-coding-agent/Cargo.toml crates/rhema-coding-agent/Cargo.toml
|
|
COPY crates/rhema-config/Cargo.toml crates/rhema-config/Cargo.toml
|
|
COPY crates/rhema-core/Cargo.toml crates/rhema-core/Cargo.toml
|
|
COPY crates/rhema-identity/Cargo.toml crates/rhema-identity/Cargo.toml
|
|
COPY crates/rhema-memory/Cargo.toml crates/rhema-memory/Cargo.toml
|
|
COPY crates/rhema-mcp/Cargo.toml crates/rhema-mcp/Cargo.toml
|
|
COPY crates/rhema-provider/Cargo.toml crates/rhema-provider/Cargo.toml
|
|
COPY crates/rhema-provider-anthropic/Cargo.toml crates/rhema-provider-anthropic/Cargo.toml
|
|
COPY crates/rhema-provider-chatgpt/Cargo.toml crates/rhema-provider-chatgpt/Cargo.toml
|
|
COPY crates/rhema-provider-copilot/Cargo.toml crates/rhema-provider-copilot/Cargo.toml
|
|
COPY crates/rhema-provider-google/Cargo.toml crates/rhema-provider-google/Cargo.toml
|
|
COPY crates/rhema-provider-llama-cpp/Cargo.toml crates/rhema-provider-llama-cpp/Cargo.toml
|
|
COPY crates/rhema-provider-openai-compatible/Cargo.toml crates/rhema-provider-openai-compatible/Cargo.toml
|
|
COPY crates/rhema-provider-opencode/Cargo.toml crates/rhema-provider-opencode/Cargo.toml
|
|
COPY crates/rhema-provider-openrouter/Cargo.toml crates/rhema-provider-openrouter/Cargo.toml
|
|
COPY crates/rhema-provider-registry/Cargo.toml crates/rhema-provider-registry/Cargo.toml
|
|
COPY crates/rhema-rsd/Cargo.toml crates/rhema-rsd/Cargo.toml
|
|
COPY crates/rhema-sandbox/Cargo.toml crates/rhema-sandbox/Cargo.toml
|
|
COPY crates/rhema-sandbox-docker/Cargo.toml crates/rhema-sandbox-docker/Cargo.toml
|
|
COPY crates/rhema-sandbox-ecrou/Cargo.toml crates/rhema-sandbox-ecrou/Cargo.toml
|
|
COPY crates/rhema-sandbox-http/Cargo.toml crates/rhema-sandbox-http/Cargo.toml
|
|
COPY crates/rhema-sandbox-local/Cargo.toml crates/rhema-sandbox-local/Cargo.toml
|
|
COPY crates/rhema-sandbox-lxd/Cargo.toml crates/rhema-sandbox-lxd/Cargo.toml
|
|
COPY crates/rhema-sandbox-server/Cargo.toml crates/rhema-sandbox-server/Cargo.toml
|
|
COPY crates/rhema-sandbox-ssh/Cargo.toml crates/rhema-sandbox-ssh/Cargo.toml
|
|
COPY crates/rhema-subagents/Cargo.toml crates/rhema-subagents/Cargo.toml
|
|
COPY crates/rhema-transcript/Cargo.toml crates/rhema-transcript/Cargo.toml
|
|
COPY integrations/rhema-calendar-integration/Cargo.toml integrations/rhema-calendar-integration/Cargo.toml
|
|
COPY integrations/rhema-forge-integration/Cargo.toml integrations/rhema-forge-integration/Cargo.toml
|
|
# rhema-claw path-depends on the tools contract, which lives in its own
|
|
# workspace: the member manifests inherit from that root, so the root
|
|
# rides along with them.
|
|
COPY tools/Cargo.toml tools/Cargo.toml
|
|
COPY tools/contract/Cargo.toml tools/contract/Cargo.toml
|
|
COPY tools/common/Cargo.toml tools/common/Cargo.toml
|
|
COPY tools/notify/Cargo.toml tools/notify/Cargo.toml
|
|
|
|
|
|
# Resolution reads the vendored crossterm (the root manifest path-patches
|
|
# to it) even though the image builds the daemon, which does not link it.
|
|
COPY vendor/ vendor/
|
|
|
|
# Cargo validates explicit target
|
|
# paths at manifest-parse time, so the fetch needs stub files at every
|
|
# declared target path: src/ for every member, plus examples/tests/benches
|
|
# as declared (default path from the target name, or the explicit `path`).
|
|
# The loop drives off the crates/*/ and integrations/*/ globs because
|
|
# neither glob's */src matches anything until the stub exists — an
|
|
# unmatched glob would hand mkdir a literal path. The real sources land
|
|
# below. (The awk program must stay on one line: a Dockerfile line not
|
|
# ending in a backslash ends the RUN.)
|
|
RUN for crate in crates/*/ integrations/*/ tools/*/; do \
|
|
mkdir -p "$crate/src"; \
|
|
touch "$crate/src/lib.rs" "$crate/src/main.rs"; \
|
|
awk -F'"' '/^\[/ { d = ""; p = 0; if ($0 == "[[example]]") { d = "examples" } else if ($0 == "[[test]]") { d = "tests" } else if ($0 == "[[bench]]") { d = "benches" }; next } d == "" { next } /^name = / && p == 0 { print d "/" $2 ".rs" } /^path = / { p = 1; print $2 }' "$crate/Cargo.toml" \
|
|
| while IFS= read -r target; do \
|
|
mkdir -p "$crate$(dirname "$target")"; \
|
|
touch "$crate$target"; \
|
|
done; \
|
|
done \
|
|
&& cargo fetch
|
|
|
|
# Sources. target/ and .git never land here: the repo-root .dockerignore
|
|
# keeps them out of the context.
|
|
COPY . .
|
|
RUN cargo build --release -p rhema-claw
|
|
|
|
FROM debian:stable-slim
|
|
|
|
# bash and git are load-bearing, not conveniences: the coding agent shells
|
|
# out via `bash -c` and clones/fetches in its per-thread workspaces.
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends bash git ca-certificates \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Numeric USER: Kubernetes cannot verify runAsNonRoot against a name.
|
|
RUN useradd --uid 1000 --user-group --create-home claw \
|
|
&& install -d -o 1000 -g 1000 /config /data
|
|
|
|
COPY --from=builder /build/target/release/rhema-claw /usr/local/bin/rhema-claw
|
|
|
|
# The chart passes --config /config/claw.yaml (the ConfigMap mount) and
|
|
# mounts the state PVC at /data; these defaults serve a bare `docker run`.
|
|
ENV XDG_CONFIG_HOME=/config \
|
|
XDG_STATE_HOME=/data/state
|
|
VOLUME /data
|
|
EXPOSE 7799
|
|
|
|
USER 1000:1000
|
|
ENTRYPOINT ["/usr/local/bin/rhema-claw"]
|