diff --git a/Dockerfile b/Dockerfile index 3395a8d..d3d935d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,97 +1,28 @@ -# Build openclaw from source to avoid npm packaging gaps (some dist files are not shipped). -FROM node:24.1.0-bookworm AS openclaw-build +FROM node:24-bookworm -# Dependencies needed for openclaw build RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ git \ - ca-certificates \ - curl \ - python3 \ - make \ - g++ \ - && rm -rf /var/lib/apt/lists/* - -# Install Bun (openclaw build uses it) -RUN curl -fsSL https://bun.sh/install | bash -ENV PATH="/root/.bun/bin:${PATH}" - -RUN corepack enable - -WORKDIR /openclaw - -# Pin to a known ref (tag/branch). If it doesn't exist, fall back to main. -ARG OPENCLAW_GIT_REF=main -RUN git clone --depth 1 --branch "${OPENCLAW_GIT_REF}" https://github.com/openclaw/openclaw.git . - -# Patch: relax version requirements for packages that may reference unpublished versions. -# Apply to all extension package.json files to handle workspace protocol (workspace:*). -RUN set -eux; \ - find ./extensions -name 'package.json' -type f | while read -r f; do \ - sed -i -E 's/"openclaw"[[:space:]]*:[[:space:]]*">=[^"]+"/"openclaw": "*"/g' "$f"; \ - sed -i -E 's/"openclaw"[[:space:]]*:[[:space:]]*"workspace:[^"]+"/"openclaw": "*"/g' "$f"; \ - done - -RUN pnpm install --no-frozen-lockfile -RUN pnpm build -ENV OPENCLAW_PREFER_PNPM=1 -RUN pnpm ui:install && pnpm ui:build - - -# Runtime image -FROM node:24.1.0-bookworm -ENV NODE_ENV=production - -RUN apt-get update \ - && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - ca-certificates \ - curl \ - build-essential \ - gcc \ - g++ \ - make \ procps \ - file \ - git \ - python3 \ - pkg-config \ - sudo \ && rm -rf /var/lib/apt/lists/* -# Install Homebrew (must run as non-root user) -# Create a user for Homebrew installation, install it, then make it accessible to all users -RUN useradd -m -s /bin/bash linuxbrew \ - && echo 'linuxbrew ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers - -USER linuxbrew -RUN NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - -USER root -RUN chown -R root:root /home/linuxbrew/.linuxbrew -ENV PATH="/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:${PATH}" - -RUN corepack enable +RUN npm install -g openclaw@latest WORKDIR /app -# Wrapper deps COPY package.json pnpm-lock.yaml ./ -RUN pnpm install --frozen-lockfile --prod - -# Copy built openclaw -COPY --from=openclaw-build /openclaw /openclaw - -# Provide an openclaw executable -RUN printf '%s\n' '#!/usr/bin/env bash' 'exec node /openclaw/dist/entry.js "$@"' > /usr/local/bin/openclaw \ - && chmod +x /usr/local/bin/openclaw +RUN corepack enable && pnpm install --frozen-lockfile --prod COPY src ./src RUN useradd -m -s /bin/bash openclaw \ - && chown -R openclaw:openclaw /app /openclaw \ + && chown -R openclaw:openclaw /app \ && mkdir -p /data && chown openclaw:openclaw /data ENV PORT=8080 +ENV OPENCLAW_ENTRY=/usr/local/lib/node_modules/openclaw/dist/entry.js EXPOSE 8080 HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \ diff --git a/src/public/setup.html b/src/public/setup.html index 61fec55..10ed012 100644 --- a/src/public/setup.html +++ b/src/public/setup.html @@ -6,8 +6,17 @@ OpenClaw Setup + -

OpenClaw Setup

+
+

OpenClaw Setup

+ +

This wizard configures OpenClaw by running the same onboarding command it uses in the terminal, but from the browser.

@@ -271,18 +289,18 @@ left: 0; right: 0; bottom: 0; - background: rgba(0, 0, 0, 0.5); + background: var(--overlay-bg); display: flex; align-items: center; justify-content: center; z-index: 1000; } .modal { - background: #1a1a2e; + background: var(--modal-bg); padding: 1.5rem; border-radius: 8px; min-width: 300px; - border: 1px solid #333; + border: 1px solid var(--modal-border); } .modal h3 { margin-top: 0; diff --git a/src/public/styles.css b/src/public/styles.css index a647c1f..e06fe25 100644 --- a/src/public/styles.css +++ b/src/public/styles.css @@ -1,24 +1,94 @@ +:root { + --bg-primary: #050810; + --bg-card: #111827; + --bg-input: #1f2937; + --bg-code: #1f2937; + --bg-pre: #0d1117; + --border-color: #1f2937; + --border-input: #374151; + --text-primary: #f0f4ff; + --text-muted: #8892b0; + --text-code: #00e5cc; + --accent: #ff4d4d; + --accent-hover: #e63946; + --secondary-bg: #1f2937; + --secondary-hover: #374151; + --tertiary-bg: #374151; + --tertiary-hover: #4b5563; + --modal-bg: #1a1a2e; + --modal-border: #333; + --overlay-bg: rgba(0, 0, 0, 0.5); +} + +[data-theme="light"] { + --bg-primary: #f8fafc; + --bg-card: #ffffff; + --bg-input: #f1f5f9; + --bg-code: #e2e8f0; + --bg-pre: #f1f5f9; + --border-color: #e2e8f0; + --border-input: #cbd5e1; + --text-primary: #1e293b; + --text-muted: #64748b; + --text-code: #0d9488; + --accent: #dc2626; + --accent-hover: #b91c1c; + --secondary-bg: #e2e8f0; + --secondary-hover: #cbd5e1; + --tertiary-bg: #cbd5e1; + --tertiary-hover: #94a3b8; + --modal-bg: #ffffff; + --modal-border: #e2e8f0; + --overlay-bg: rgba(0, 0, 0, 0.3); +} + +@media (prefers-color-scheme: light) { + :root:not([data-theme="dark"]) { + --bg-primary: #f8fafc; + --bg-card: #ffffff; + --bg-input: #f1f5f9; + --bg-code: #e2e8f0; + --bg-pre: #f1f5f9; + --border-color: #e2e8f0; + --border-input: #cbd5e1; + --text-primary: #1e293b; + --text-muted: #64748b; + --text-code: #0d9488; + --accent: #dc2626; + --accent-hover: #b91c1c; + --secondary-bg: #e2e8f0; + --secondary-hover: #cbd5e1; + --tertiary-bg: #cbd5e1; + --tertiary-hover: #94a3b8; + --modal-bg: #ffffff; + --modal-border: #e2e8f0; + --overlay-bg: rgba(0, 0, 0, 0.3); + } +} + body { font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial; margin: 2rem; max-width: 900px; - background: #050810; - color: #f0f4ff; + background: var(--bg-primary); + color: var(--text-primary); + transition: background 0.2s, color 0.2s; } .card { - border: 1px solid #1f2937; + border: 1px solid var(--border-color); border-radius: 12px; padding: 1.25rem; margin: 1rem 0; - background: #111827; + background: var(--bg-card); + transition: background 0.2s, border-color 0.2s; } label { display: block; margin-top: 0.75rem; font-weight: 600; - color: #f0f4ff; + color: var(--text-primary); } input, @@ -26,23 +96,24 @@ select { width: 100%; padding: 0.6rem; margin-top: 0.25rem; - background: #1f2937; - border: 1px solid #374151; + background: var(--bg-input); + border: 1px solid var(--border-input); border-radius: 8px; - color: #f0f4ff; + color: var(--text-primary); + transition: background 0.2s, border-color 0.2s, color 0.2s; } input:focus, select:focus { outline: none; - border-color: #ff4d4d; + border-color: var(--accent); } button { padding: 0.8rem 1.2rem; border-radius: 10px; border: 0; - background: #ff4d4d; + background: var(--accent); color: #fff; font-weight: 700; cursor: pointer; @@ -50,30 +121,32 @@ button { } button:hover { - background: #e63946; + background: var(--accent-hover); } .secondary-button { - background: #1f2937; + background: var(--secondary-bg); + color: var(--text-primary); margin-left: 0.5rem; } .secondary-button:hover { - background: #374151; + background: var(--secondary-hover); } .tertiary-button { - background: #374151; + background: var(--tertiary-bg); + color: var(--text-primary); margin-left: 0.5rem; } .tertiary-button:hover { - background: #4b5563; + background: var(--tertiary-hover); } code { - background: #1f2937; - color: #00e5cc; + background: var(--bg-code); + color: var(--text-code); padding: 0.1rem 0.3rem; border-radius: 6px; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; @@ -81,31 +154,51 @@ code { pre { white-space: pre-wrap; - background: #0d1117; - border: 1px solid #1f2937; + background: var(--bg-pre); + border: 1px solid var(--border-color); border-radius: 8px; padding: 1rem; margin-top: 1rem; - color: #8892b0; + color: var(--text-muted); font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.875rem; overflow-x: auto; + transition: background 0.2s, border-color 0.2s, color 0.2s; } a { - color: #00e5cc; + color: var(--text-code); text-decoration: none; transition: color 0.2s; } a:hover { - color: #ff4d4d; + color: var(--accent); } .muted { - color: #8892b0; + color: var(--text-muted); } h1, h2, h3 { - color: #f0f4ff; + color: var(--text-primary); +} + +.header-row { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.5rem; +} + +.theme-toggle { + background: var(--secondary-bg); + color: var(--text-primary); + padding: 0.5rem 0.75rem; + font-size: 0.875rem; + border-radius: 8px; +} + +.theme-toggle:hover { + background: var(--secondary-hover); }