diff --git a/docker/Dockerfile b/docker/Dockerfile index b6d55d7..a1c5826 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ -ARG OPENCLAW_VERSION=2026.4.11 -ARG OLLAMA_VERSION=0.20.4 +ARG OPENCLAW_VERSION=2026.4.15 +ARG OLLAMA_VERSION=0.21.0 ARG NODE_VERSION=24 # Use the official Ollama image to get the binary @@ -19,16 +19,21 @@ RUN ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm # Copy the Ollama binary from the official image COPY --from=ollama-bin --chown=abc:abc /usr/bin/ollama /usr/local/bin/ollama -RUN npm install -g openclaw@${OPENCLAW_VERSION} +RUN npm install -g openclaw@${OPENCLAW_VERSION} && \ + chown -R abc:abc /usr/local/lib/node_modules/openclaw /usr/local/bin/openclaw +COPY --chown=abc:abc OpenClaw.desktop /custom-cont-init.d/OpenClaw.desktop +COPY --chown=abc:abc init-openclaw.sh /custom-cont-init.d/init-openclaw.sh + +# Install ModelRelay and start automatically when desktop loads +RUN npm install -g modelrelay@${MODELRELAY_VERSION} && \ + chown -R abc:abc /usr/local/lib/node_modules/modelrelay /usr/local/bin/modelrelay +COPY --chown=abc:abc ModelRelay.desktop /custom-cont-init.d/ModelRelay.desktop +COPY --chown=abc:abc init-modelrelay.sh /custom-cont-init.d/init-modelrelay.sh +COPY --chown=abc:abc common.sh /custom-cont-init.d/common.sh # Start Ollama to start automatically when the desktop loads, RUN mkdir -p /custom-cont-init.d RUN echo "runuser -l abc -c 'ollama serve &'" > /custom-cont-init.d/start-ollama.sh && \ chmod +x /custom-cont-init.d/start-ollama.sh -RUN echo "rm -rf /config/.npm" >> /custom-cont-init.d/init-openclaw.sh && \ - echo "chown abc:abc -R /usr/local/lib/node_modules &" >> /custom-cont-init.d/init-openclaw.sh && \ - echo "chown abc:abc -R /usr/local/bin &" >> /custom-cont-init.d/init-openclaw.sh && \ - chmod +x /custom-cont-init.d/init-openclaw.sh - -EXPOSE 11434 +COPY --chown=abc:abc init-openclaw.sh /custom-cont-init.d/init-openclaw.sh diff --git a/docker/ModelRelay.desktop b/docker/ModelRelay.desktop new file mode 100644 index 0000000..a88a978 --- /dev/null +++ b/docker/ModelRelay.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Terminal=false +Exec=mate-terminal --title="ModelRelay" -e "bash -c 'modelrelay; exec bash'" +Name[en_US]=ModelRelay +Name=ModelRelay +GenericName[en_US.UTF-8]=ModelRelay \ No newline at end of file diff --git a/docker/OpenClaw.desktop b/docker/OpenClaw.desktop new file mode 100644 index 0000000..7bdb114 --- /dev/null +++ b/docker/OpenClaw.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Terminal=false +Exec=mate-terminal --title="OpenClaw" -e "bash -c 'openclaw gateway --port 18789 --allow-unconfigured --verbose; exec bash'" +Name[en_US]=OpenClaw +Name=PicoClaw +GenericName[en_US.UTF-8]=OpenClaw Gateway \ No newline at end of file diff --git a/docker/common.sh b/docker/common.sh new file mode 100755 index 0000000..21676b9 --- /dev/null +++ b/docker/common.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# Function to safely sync a desktop file if it has changed +# Usage: sync_desktop_file +sync_desktop_file() { + local SRC="$1" + local DEST="$2" + local DEST_DIR + local DEST_BASE + local TMP_DEST + + if [ ! -r "$SRC" ]; then + echo "Error: source file $SRC is missing or not readable." >&2 + return 1 + fi + + DEST_DIR="$(dirname "$DEST")" + DEST_BASE="$(basename "$DEST")" + + # Ensure directory exists and has correct ownership + mkdir -p "$DEST_DIR" + chown abc:abc "$DEST_DIR" + + TMP_DEST="$(mktemp "${DEST_DIR}/.${DEST_BASE}.tmp.XXXXXX")" || return 1 + + if [ -f "$DEST" ]; then + # Check if the file content is different + if ! cmp -s "$SRC" "$DEST"; then + echo "Updating $DEST (content changed). Preparing replacement" + if ! cp "$SRC" "$TMP_DEST"; then + rm -f "$TMP_DEST" + return 1 + fi + if ! chown abc:abc "$TMP_DEST"; then + rm -f "$TMP_DEST" + return 1 + fi + # Use a backup just in case, but overwrite it next time + mv "$DEST" "${DEST}.bak" 2>/dev/null || true + mv "$TMP_DEST" "$DEST" + else + echo "$DEST is already up to date." + rm -f "$TMP_DEST" + fi + else + echo "Creating $DEST" + if ! cp "$SRC" "$TMP_DEST"; then + rm -f "$TMP_DEST" + return 1 + fi + if ! chown abc:abc "$TMP_DEST"; then + rm -f "$TMP_DEST" + return 1 + fi + mv "$TMP_DEST" "$DEST" + fi +} \ No newline at end of file diff --git a/docker/init-modelrelay.sh b/docker/init-modelrelay.sh new file mode 100755 index 0000000..e0ac8d9 --- /dev/null +++ b/docker/init-modelrelay.sh @@ -0,0 +1,63 @@ +#!/bin/bash +source /custom-cont-init.d/common.sh || exit 1 + +SRC="/custom-cont-init.d/ModelRelay.desktop" + +configure_open_claw() { + local config_path="$1" + echo "[init-modelrelay] Configuring OpenClaw at $config_path" + + tmp_file=$(mktemp) + jq \ + --arg baseUrl "http://127.0.0.1:7352/v1" \ + ' + # Ensure base paths exist + .models //= {} | + .models.providers //= {} | + .agents //= {} | + .agents.defaults //= {} | + .agents.defaults.model //= {} | + .agents.defaults.models //= {} | + .agents.defaults.models["modelrelay/auto-fastest"] //= {} | + + # Logic for config.models.providers.modelrelay + .models.providers.modelrelay = { + "baseUrl": $baseUrl, + "api": "openai-completions", + "apiKey": "no-key", + "models": [ + { "id": "auto-fastest", "name": "Auto Fastest" } + ] + } | + + # Logic for config.agents.defaults.model.primary + .agents.defaults.model.primary = "modelrelay/auto-fastest" + + ' "$config_path" > "$tmp_file" && mv "$tmp_file" "$config_path" + # cat "$config_path" + echo "Success: Configured $config_path" + return 0 +} + +# Prep nodejs npm for ModelRelay +rm -rf /config/.npm +chown abc:abc -R /usr/local/lib/node_modules & +chown abc:abc -R /usr/local/bin & + +# Sync desktop file for autostart and desktop icon +sync_desktop_file "$SRC" "/config/.config/autostart/ModelRelay.desktop" +sync_desktop_file "$SRC" "/config/Desktop/ModelRelay.desktop" + +# Add modelrelay model to openclaw's config as soon as it appears, and set it as default for agents if no default was set +# /config/.openclaw/openclaw.json +( + for i in {0..120}; do + echo "[init-modelrelay] Waiting to configure OpenClaw..." + if [ -f "/config/.openclaw/openclaw.json.bak" ] && [ -f "/config/.openclaw/openclaw.json" ]; then + configure_open_claw "/config/.openclaw/openclaw.json" + chown abc:abc "/config/.openclaw/openclaw.json" + break + fi + sleep 5 + done +) & \ No newline at end of file diff --git a/docker/init-openclaw.sh b/docker/init-openclaw.sh new file mode 100755 index 0000000..be59fec --- /dev/null +++ b/docker/init-openclaw.sh @@ -0,0 +1,7 @@ +#!/bin/bash +source /custom-cont-init.d/common.sh || exit 1 + +SRC="/custom-cont-init.d/OpenClaw.desktop" + +sync_desktop_file "$SRC" "/config/.config/autostart/OpenClaw.desktop" +sync_desktop_file "$SRC" "/config/Desktop/OpenClaw.desktop" \ No newline at end of file