Simplify install and docker setup

This commit is contained in:
Arjun Komath
2026-01-31 20:53:47 +11:00
parent 74aad02721
commit 8917271288
3 changed files with 146 additions and 104 deletions
+7 -76
View File
@@ -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 \
+22 -4
View File
@@ -6,8 +6,17 @@
<title>OpenClaw Setup</title>
<link rel="stylesheet" href="/setup/styles.css" />
<script>
(function() {
const saved = localStorage.getItem('theme');
if (saved) {
document.documentElement.setAttribute('data-theme', saved);
}
})();
</script>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('setupApp', () => ({
theme: localStorage.getItem('theme') || (window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'),
status: 'Loading...',
configured: false,
openclawVersion: '',
@@ -36,6 +45,12 @@
await this.refreshStatus();
},
toggleTheme() {
this.theme = this.theme === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', this.theme);
localStorage.setItem('theme', this.theme);
},
async httpJson(url, opts = {}) {
opts.credentials = 'same-origin';
const res = await fetch(url, opts);
@@ -165,7 +180,10 @@
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.8/dist/cdn.min.js"></script>
</head>
<body x-data="setupApp()">
<h1>OpenClaw Setup</h1>
<div class="header-row">
<h1>OpenClaw Setup</h1>
<button @click="toggleTheme()" class="theme-toggle" x-text="theme === 'dark' ? 'Light mode' : 'Dark mode'"></button>
</div>
<p class="muted">This wizard configures OpenClaw by running the same onboarding command it uses in the terminal, but from the browser.</p>
<div class="card">
@@ -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;
+117 -24
View File
@@ -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);
}