添加qiming-rcoder模块

This commit is contained in:
Codex
2026-06-01 13:54:52 +08:00
parent 8092c4b1f8
commit 4b1a580132
539 changed files with 151650 additions and 0 deletions

View File

@@ -0,0 +1,887 @@
# ============================================================================
# 基础镜像 Dockerfile - rcoder-agent-base
# ============================================================================
# 此镜像包含所有系统软件、工具链和环境配置,不包含业务代码
# 构建命令: make docker-build-agent-base
# 平时不需要重新构建,只有在修改系统依赖时才需要重新构建
# ============================================================================
ARG DOCKER_MIRROR
# GitHub 镜像加速配置(默认启用)
ARG USE_GITHUB_MIRROR=true
# 基础镜像: buildpack-deps:bookworm-curl
# - 基于 debian:12 (bookworm), 与 debian:12 100% 兼容
# - 预装 curl + wget + ca-certificates + gnupg, 无需先 apt 安装
# - Docker 官方维护, 与 debian:12 共享底层 layer, 拉取几乎零增量
# - 避免"先用海外 apt 源装 curl 再配置镜像"的鸡生蛋死循环
FROM ${DOCKER_MIRROR}buildpack-deps:bookworm-curl
# GitHub 镜像加速环境变量(必须在 FROM 之后设置)
ENV GITHUB_MIRROR_URL=https://gh-proxy.org/https://github.com/
# Base image with desktop environment and applications
# Environment variables:
ENV \
# Avoid system prompts: \
DEBIAN_FRONTEND=noninteractive \
DEBIAN_PRIORITY=high \
# Timezone: \
TZ=Asia/Shanghai \
# Pip settings: \
PIP_DEFAULT_TIMEOUT=100 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
PIP_NO_CACHE_DIR=1
# 使用 LinuxMirrors 一键配置阿里云镜像源
# 参考: https://github.com/SuperManito/LinuxMirrors
# buildpack-deps:bookworm-curl 已预装 curl可直接执行脚本配置镜像源
RUN curl -sSL https://linuxmirrors.cn/main.sh | bash -s -- \
--source mirrors.aliyun.com \
--protocol https \
--use-intranet-source false \
--install-epel false \
--backup false \
--upgrade-software false \
--clean-cache true \
--ignore-backup-tips
# 复制 npmrc 配置镜像(使用国内镜像加速)
COPY .npmrc /root/.npmrc
# Desktop environment and applications (merged for smaller image size):
RUN apt-get update && \
# Install all packages in one layer to reduce image size
apt-get install -y \
# X window server:
xserver-xorg xorg x11-xserver-utils xvfb x11-utils xauth \
# XFCE desktop environment:
xfce4 xfce4-goodies \
# 中文输入法fcitx5 完整安装)
fcitx5 fcitx5-pinyin fcitx5-frontend-gtk3 fcitx5-frontend-gtk2 fcitx5-frontend-qt5 fcitx5-module-xorg fcitx5-config-qt im-config dbus-x11 \
# Basic system utilities:
util-linux sudo curl git \
# Python pip:
python3-pip \
# Network analysis tools:
iputils-ping traceroute dnsutils tcpdump mtr-tiny nmap iperf3 httping telnet \
# Desktop SDK tools:
xdotool scrot \
# FFmpeg 8 编译依赖:
yasm nasm pkg-config libx264-dev libx265-dev libnuma-dev libvpx-dev libmp3lame-dev libopus-dev libspeex-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev texinfo zlib1g-dev \
# VNC and streaming:
tigervnc-standalone-server tigervnc-xorg-extension net-tools netcat-openbsd \
# Audio streaming (PulseAudio + pcmflux dependencies):
pulseaudio pulseaudio-utils libpulse-dev libopus-dev \
# Standard applications:
x11-apps xpdf gedit xpaint tint2 galculator pcmanfm \
# Dependencies for later installations:
apt-transport-https ca-certificates gnupg \
socat supervisor \
xclip \
wget software-properties-common \
# Suna dependencies:
poppler-utils wkhtmltopdf antiword unrtf catdoc gawk file jq csvkit xmlstarlet zip unzip tmux vim tree rsync && \
# Configure X11 wrapper to allow any user:
sed -i 's/allowed_users=console/allowed_users=anybody/' /etc/X11/Xwrapper.config && \
# Set the default terminal
ln -sf /usr/bin/xfce4-terminal.wrapper /etc/alternatives/x-terminal-emulator
# ============================================================================
# 🎬 编译安装 FFmpeg 8.0 (最新版本)
# ============================================================================
# Debian 12 官方仓库的 FFmpeg 版本是 5.1.8,这里从源码编译安装最新的 FFmpeg 8
RUN echo "Building FFmpeg 8.0 from source..." && \
cd /tmp && \
if [ "${USE_GITHUB_MIRROR}" = "true" ]; then \
git clone --depth 1 --branch release/8.0 ${GITHUB_MIRROR_URL}FFmpeg/FFmpeg.git ffmpeg-8.0; \
else \
git clone --depth 1 --branch release/8.0 https://github.com/FFmpeg/FFmpeg.git ffmpeg-8.0; \
fi && \
cd ffmpeg-8.0 && \
./configure \
--extra-version="8.0" \
--disable-debug \
--disable-doc \
--disable-ffplay \
--enable-gpl \
--enable-libx264 \
--enable-libx265 \
--enable-libvpx \
--enable-libmp3lame \
--enable-libopus && \
make -j$(nproc) && \
make install && \
ldconfig && \
echo "✅ FFmpeg 8.0 installed: $(ffmpeg -version | head -1)" && \
# Clean up build artifacts:
rm -rf /tmp/ffmpeg-8.0 && \
# Clean up package cache and temporary files to reduce image size:
apt-get autoremove -y && \
apt-get autoclean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /root/.cache
# ============================================================================
# 🔊 配置 PulseAudio 系统模式(容器环境优化)
# ============================================================================
# 配置 PulseAudio 系统模式,支持以 root 身份运行
RUN mkdir -p /etc/pulse && \
# 创建系统模式配置
printf '[General]\n\
default-sample-format = s16le\n\
default-sample-rate = 48000\n\
default-sample-channels = 2\n\
default-fragments = 2\n\
default-fragment-size-msec = 125\n' > /etc/pulse/daemon.conf && \
# 允许所有用户连接系统模式 PulseAudio
printf '[General]\n\
allow-autospawn-for-root = yes\n\
default-server = unix:/var/run/pulse/native\n' > /etc/pulse/client.conf && \
# 创建系统模式 pulse 用户组
groupadd -r pulse-access 2>/dev/null || true && \
usermod -aG pulse-access root 2>/dev/null || true && \
usermod -aG pulse-access user 2>/dev/null || true && \
# 创建 PulseAudio 运行时目录
mkdir -p /var/run/pulse && \
chmod 1777 /var/run/pulse && \
echo "✅ PulseAudio system mode configured"
# ============================================================================
# 🐍 配置 pip 镜像源 (系统级配置,所有用户共享)
# ============================================================================
# 使用阿里云 PyPI 镜像源(稳定可靠,无限流限制)
RUN mkdir -p /etc/pip && \
printf '[global]\n\
index-url = https://mirrors.aliyun.com/pypi/simple/\n\
trusted-host = mirrors.aliyun.com\n\
timeout = 120\n\
\n\
[install]\n\
trusted-host = mirrors.aliyun.com\n' > /etc/pip/pip.conf && \
# 同时为 root 用户创建配置(备用)
mkdir -p /root/.config/pip && \
cp /etc/pip/pip.conf /root/.config/pip/pip.conf && \
# 为 user 用户创建配置目录(后续会设置权限)
mkdir -p /home/user/.config/pip && \
cp /etc/pip/pip.conf /home/user/.config/pip/pip.conf && \
echo "✅ pip 镜像源配置完成: https://mirrors.aliyun.com/pypi/simple/"
# ============================================================================
# 📦 安装常用 Python 数据科学库 (系统级,所有用户可用)
# ============================================================================
# 🎯 最佳实践:指定稳定版本,避免版本冲突
# 1. 升级基础工具确保依赖解析正确
# 2. 按依赖关系顺序安装numpy -> pandas/scipy -> matplotlib -> 其他)
# 3. 使用经过验证的稳定版本组合
# 🎯 使用 requirements.txt 方式安装依赖
# ======================================
# 优势:版本锁定、可重现构建、便于维护
# 复制 requirements.txt 文件到容器
COPY requirements.txt /tmp/requirements.txt
# 升级基础工具并安装依赖
RUN pip3 install --break-system-packages --upgrade pip setuptools wheel && \
pip3 install --break-system-packages -r /tmp/requirements.txt && \
echo "✅ Python 数据科学库安装完成" && \
echo "已安装的包:" && \
pip3 list | grep -E '(numpy|pandas|scipy|matplotlib|openpyxl|python-docx|PyMuPDF|xlrd|xlwt|seaborn|requests|BeautifulSoup|lxml|scikit-learn)'
# ============================================================================
# 📺 noVNC 远程桌面客户端(based on official v1.6.0, with customized vnc.html):
# ============================================================================
# 添加构建参数以强制打破缓存(每次构建时传入不同的 CACHEBUST_NOVNC
ARG CACHEBUST_NOVNC=1
RUN echo "noVNC cache bust: ${CACHEBUST_NOVNC}" && \
if [ "${USE_GITHUB_MIRROR}" = "true" ]; then \
echo "Using GitHub mirror: ${GITHUB_MIRROR_URL}"; \
echo "Cloning noVNC from: ${GITHUB_MIRROR_URL}nuwax-ai/noVNC.git"; \
git clone --depth 1 -b release ${GITHUB_MIRROR_URL}nuwax-ai/noVNC.git /opt/noVNC && \
echo "Cloning websockify from: ${GITHUB_MIRROR_URL}novnc/websockify"; \
git clone --depth 1 -b v0.12.0 ${GITHUB_MIRROR_URL}novnc/websockify /opt/noVNC/utils/websockify; \
else \
echo "Using direct GitHub (no mirror)"; \
git clone --depth 1 -b release https://github.com/nuwax-ai/noVNC.git /opt/noVNC && \
git clone --depth 1 -b v0.12.0 https://github.com/novnc/websockify /opt/noVNC/utils/websockify; \
fi && \
cd /opt/noVNC && \
ln -s /opt/noVNC/vnc.html /opt/noVNC/index.html
# ============================================================================
# Install LibreOffice
# ============================================================================
# Installation logic:
# 1. If local tar.gz exists in downloads/ (pre-downloaded via Makefile) -> use it
# 2. Otherwise -> download from official URL (fallback)
#
# This allows:
# - Fast build with pre-downloaded files (avoid slow external download)
# - Fallback to online download if local file is missing
# ============================================================================
ARG LIBREOFFICE_FILE
ARG TARGETARCH
COPY downloads /tmp/downloads
# Determine URL based on architecture
RUN echo "Installing LibreOffice..." && \
case "${TARGETARCH}" in \
"amd64") \
LIBREOFFICE_URL="https://download.documentfoundation.org/libreoffice/stable/25.8.5/deb/x86_64/${LIBREOFFICE_FILE}" \
;; \
"arm64") \
LIBREOFFICE_URL="https://download.documentfoundation.org/libreoffice/stable/25.8.5/deb/aarch64/${LIBREOFFICE_FILE}" \
;; \
*) echo "❌ Unsupported architecture: ${TARGETARCH}" && exit 1 ;; \
esac && \
# Check if local pre-downloaded file exists
if [ -f "/tmp/downloads/${LIBREOFFICE_FILE}" ]; then \
echo "📦 Using pre-downloaded file: /tmp/downloads/${LIBREOFFICE_FILE}"; \
cp /tmp/downloads/${LIBREOFFICE_FILE} /tmp/libreoffice.tar.gz; \
else \
echo "🌐 No local file found, downloading from official URL..."; \
wget -q -O /tmp/libreoffice.tar.gz "${LIBREOFFICE_URL}" || { \
echo "❌ ERROR: Failed to download LibreOffice from ${LIBREOFFICE_URL}"; \
exit 1; \
}; \
fi && \
# Extract and install
cd /tmp && \
tar -xzf libreoffice.tar.gz && \
cd LibreOffice_*/DEBS && \
dpkg -i *.deb && \
apt-get update && \
apt-get install -f -y && \
# Create symlinks for LibreOffice executables
LIBREOFFICE_BIN=$(find /opt -name "soffice" -path "*/program/*" 2>/dev/null | head -1) && \
if [ -n "$LIBREOFFICE_BIN" ]; then \
LIBREOFFICE_DIR=$(dirname "$LIBREOFFICE_BIN") && \
ln -sf "$LIBREOFFICE_BIN" /usr/bin/soffice && \
ln -sf "$LIBREOFFICE_DIR/libreoffice" /usr/bin/libreoffice 2>/dev/null || true && \
echo "✅ LibreOffice installed: $LIBREOFFICE_BIN -> /usr/bin/soffice"; \
else \
echo "❌ ERROR: LibreOffice soffice binary not found in /opt"; \
exit 1; \
fi && \
# Clean up
rm -rf /tmp/LibreOffice_* /tmp/libreoffice.tar.gz /tmp/downloads && \
apt-get autoremove -y && \
apt-get autoclean && \
rm -rf /var/lib/apt/lists/*
# User applications:
# ~ Make your changes to this template BELOW this line ~
# Install Chromium from Debian official repository (optimized single layer)
RUN apt-get update && \
apt-get install -y chromium chromium-driver && \
# Clean up package cache to reduce image size:
apt-get autoremove -y && \
apt-get autoclean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Configure Chromium to skip first-run experience and enable CDP
RUN mkdir -p /etc/chromium/policies/managed/ && \
echo '{"CommandLineFlagSecurityWarningsEnabled": false, "AllowInsecureLocalConnections": true, "RemoteDebuggingAllowed": true, "RemoteDebuggingPort": "9222", "RemoteDebuggingAddress":"0.0.0.0", "AutoplayAllowed": true, "MetricsReportingEnabled": false, "BrowserSignin": 0, "SyncDisabled": true, "PromotionalTabsEnabled": false, "DefaultBrowserSettingEnabled": false, "DefaultSearchProviderEnabled": false, "HideWebStoreIcon": true, "PasswordManagerEnabled": false, "PromptForDownloadLocation": false, "SafeBrowsingEnabled": false, "SafeBrowsingExtendedReportingEnabled": false, "UrlKeyedAnonymizedDataCollectionEnabled": false, "CloudPrintSubmitEnabled": false, "CloudManagementEnrollmentToken": "", "SpellcheckEnabled": false}' > /etc/chromium/policies/managed/policies.json
# Create user and configure Chrome (merged for smaller image size)
RUN apt-get update && \
apt-get install -y gnome-keyring policykit-1-gnome policykit-1 && \
# Create default user
useradd -ms /bin/bash user && \
usermod -aG sudo user && \
passwd -d user && \
echo "user ALL=(ALL:ALL) NOPASSWD: ALL" >>/etc/sudoers && \
# Create Chromium configuration directories
mkdir -p /home/user/.config/chromium/Default/ && \
echo '{ "browser": { "custom_chrome_frame": true, "has_seen_welcome_page": true }, "profile": { "default_content_setting_values": { "geolocation": 1 }, "exit_type": "Normal", "exited_cleanly": true, "password_manager_enabled": false }, "credentials_enable_service": false, "credentials_enable_autosignin": false }' > /home/user/.config/chromium/Default/Preferences && \
# Configure GNOME keyring with empty password (禁用密码提示)
mkdir -p /home/user/.local/share/keyrings && \
# 创建 Default keyring无密码禁用锁定
printf '[keyring]\ndisplay-name=Default keyring\nctime=1732780800\nmtime=1732780800\nlock-on-idle=false\nlock-after=false\n' > /home/user/.local/share/keyrings/Default_keyring.keyring && \
# 设置为默认 keyring
echo 'Default_keyring' > /home/user/.local/share/keyrings/default && \
chmod 600 /home/user/.local/share/keyrings/Default_keyring.keyring && \
chmod 600 /home/user/.local/share/keyrings/default && \
# Configure PolicyKit to auto-allow Chromium operations
mkdir -p /etc/polkit-1/localauthority/50-local.d && \
# Create comprehensive policy for user
printf '[Allow all for user]\nIdentity=unix-user:user\nAction=*\nResultAny=yes\nResultInactive=yes\nResultActive=yes\n\n[Allow all for user - specific patterns]\nIdentity=unix-user:user\nAction=org.freedesktop.policykit.*\nResultAny=yes\nResultInactive=yes\nResultActive=yes\n\n[Allow Chromium specific]\nIdentity=unix-user:user\nAction=org.chromium.*\nResultAny=yes\nResultInactive=yes\nResultActive=yes' > /etc/polkit-1/localauthority/50-local.d/comprehensive-allow.pkla && \
# ========== 系统级自启动配置 (不受用户目录挂载影响) ==========
mkdir -p /etc/xdg/autostart && \
# PolicyKit authentication agent autostart
printf "[Desktop Entry]\nType=Application\nName=PolicyKit Auth\nExec=/usr/lib/policykit-1-gnome/polkit-gnome-authentication-agent-1\nHidden=false\nNoDisplay=false\nX-GNOME-Autostart-enabled=true\nStartupNotify=false" > /etc/xdg/autostart/polkit-gnome-auth.desktop && \
# D-Bus session autostart
printf "[Desktop Entry]\nType=Application\nName=D-Bus Session\nExec=/usr/bin/dbus-launch --sh-syntax\nHidden=false\nNoDisplay=false\nX-GNOME-Autostart-enabled=true" > /etc/xdg/autostart/dbus-session.desktop && \
# PolicyKit daemon autostart
printf "[Desktop Entry]\nType=Application\nName=PolicyKit Daemon\nExec=/usr/lib/policykit-1/polkitd --no-debug\nHidden=false\nNoDisplay=false\nX-GNOME-Autostart-enabled=true" > /etc/xdg/autostart/polkitd.desktop && \
# Fcitx5 autostart
printf '[Desktop Entry]\nType=Application\nName=Fcitx 5\nComment=Input Method Framework\nExec=fcitx5 -d --replace\nIcon=fcitx5\nStartupNotify=false\nTerminal=false\nCategories=System;Utility;\nX-GNOME-Autostart-enabled=true\nX-XFCE-Autostart-enabled=true' > /etc/xdg/autostart/fcitx5.desktop && \
# 创建用户运行时目录
mkdir -p /run/user/$(id -u user) && \
# ========== 系统级 fcitx5 输入法配置 ==========
mkdir -p /etc/xdg/fcitx5/conf && \
# fcitx5 全局配置
printf '[Hotkey]\nEnumerateWithTriggerKeys=True\nAltTriggerKeys=\nEnumerateForwardKeys=\nEnumerateBackwardKeys=\nEnumerateSkipFirst=False\n\n[Hotkey/TriggerKeys]\n0=Control+space\n1=Shift+Control+space\n\n[Hotkey/EnumerateGroupForwardKeys]\n0=Super+space\n\n[Hotkey/EnumerateGroupBackwardKeys]\n0=Shift+Super+space\n\n[Hotkey/ActivateKeys]\n0=Hangul_Hanja\n\n[Hotkey/DeactivateKeys]\n0=Hangul_Romaja\n\n[Hotkey/PrevPage]\n0=Up\n\n[Hotkey/NextPage]\n0=Down\n\n[Hotkey/PrevCandidate]\n0=Shift+Tab\n\n[Hotkey/NextCandidate]\n0=Tab\n\n[Hotkey/TogglePreedit]\n0=Control+Alt+P\n\n[Behavior]\nActiveByDefault=False\nShareInputState=No\nPreloadInputMethod=True\nShowInputMethodInformation=True\nShowInputMethodInformationWhenFocusIn=False\nCompactInputMethodInformation=True\nShowFirstInputMethodInformation=True\nDefaultPageSize=5\nOverrideXkbOption=False\nCustomXkbOption=\nEnabledAddons=\nDisabledAddons=\nPreeditEnabledByDefault=True' > /etc/xdg/fcitx5/config && \
# 拼音配置
printf '[Addon]\nEnabled=True\n\n[Behavior]\nPreeditInApplication=False\n\n[PinyinEngine]\nShuangpinProfile=Ziranma\nShowActualPinyinInHint=False\nPredictionEnabled=True\nPredictionSize=10\nCloudPinyinEnabled=False\nCloudPinyinIndex=2' > /etc/xdg/fcitx5/conf/pinyin.conf && \
# 禁用云拼音提示
printf '[Addon]\nEnabled=False\n\n[Behavior]\nShowNotification=False' > /etc/xdg/fcitx5/conf/cloudpinyin.conf && \
# 禁用通知
printf '[Addon]\nEnabled=False\n\n[Behavior]\nHiddenNotifications=' > /etc/xdg/fcitx5/conf/notifications.conf && \
# fcitx5 profile (系统默认: 英文键盘,用户可通过 Ctrl+Space 切换到中文拼音)
printf '[Groups/0]\nName=Default\nDefault Layout=us\nDefaultIM=keyboard-us\n\n[Groups/0/Items/0]\nName=keyboard-us\nLayout=\n\n[Groups/0/Items/1]\nName=pinyin\nLayout=\n\n[GroupOrder]\n0=Default\n\n[Behavior]\nActiveByDefault=False\nShareInputState=No' > /etc/xdg/fcitx5/profile && \
# ========== 系统级环境变量配置 (替代 .bashrc/.xprofile) ==========
mkdir -p /etc/profile.d && \
printf '#!/bin/sh\n# Fcitx5 Input Method Environment Variables\nexport GTK_IM_MODULE=fcitx5\nexport QT_IM_MODULE=fcitx5\nexport XMODIFIERS=@im=fcitx5\nexport INPUT_METHOD=fcitx5\nexport SDL_IM_MODULE=fcitx5\nexport GLFW_IM_MODULE=fcitx5\n' > /etc/profile.d/fcitx5-env.sh && \
chmod +x /etc/profile.d/fcitx5-env.sh && \
# ========== 系统级 GTK 配置 ==========
mkdir -p /etc/gtk-3.0 && \
printf '[Settings]\ngtk-im-module=fcitx5' > /etc/gtk-3.0/settings.ini && \
# ========== 隐藏 root 用户警告 ==========
# 为 root 用户创建 GTK CSS 配置,彻底隐藏 Thunar 和 XFCE 的各种 root 警告
mkdir -p /root/.config/gtk-3.0 && \
printf '/* Hide Thunar root warnings completely */\n.thunar-window infobar.warning { min-height: 0; max-height: 0; padding: 0; margin: 0; opacity: 0; }\n.thunar-window infobar.warning * { min-height: 0; max-height: 0; padding: 0; margin: 0; opacity: 0; }\n.thunar-window infobar.warning button { min-height: 0; min-width: 0; max-height: 0; padding: 0; margin: 0; opacity: 0; }\ninfobar.warning { min-height: 0; max-height: 0; padding: 0; margin: 0; opacity: 0; }\ninfobar.warning * { min-height: 0; max-height: 0; padding: 0; margin: 0; opacity: 0; }\n/* Hide XFCE root warning */\n.root-warning { display: none !important; opacity: 0 !important; }\n.root-warning * { display: none !important; opacity: 0 !important; }\n' > /root/.config/gtk-3.0/gtk.css && \
# 同时为 /home/user 添加配置(因为 HOME=/home/user
mkdir -p /home/user/.config/gtk-3.0 && \
cp /root/.config/gtk-3.0/gtk.css /home/user/.config/gtk-3.0/gtk.css && \
# 禁用 XFCE 会话的 root 警告(系统级配置)
mkdir -p /root/.config/xfce4/xfconf/xfce-perchannel-xml && \
echo '<?xml version="1.0" encoding="UTF-8"?>\n<channel name="xfce4-session" version="1.0">\n <property name="general" type="empty">\n <property name="ShowRootWarning" type="bool" value="false"/>\n </property>\n</channel>' > /root/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-session.xml && \
# 同时为 user 配置
mkdir -p /home/user/.config/xfce4/xfconf/xfce-perchannel-xml && \
cp /root/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-session.xml /home/user/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-session.xml && \
# ========== 系统级 im-config ==========
echo 'fcitx5' > /etc/X11/xinit/xinputrc 2>/dev/null || true && \
# ========== 用户目录 - 只保留需要持久化的配置 ==========
# Chromium 配置 (用户可能需要持久化浏览器数据)
mkdir -p /home/user/.config/chromium/Default/ && \
echo '{ "browser": { "custom_chrome_frame": true, "has_seen_welcome_page": true }, "profile": { "default_content_setting_values": { "geolocation": 1 }, "exit_type": "Normal", "exited_cleanly": true, "password_manager_enabled": false }, "credentials_enable_service": false, "credentials_enable_autosignin": false }' > /home/user/.config/chromium/Default/Preferences && \
# GNOME keyring (禁用密码提示)
mkdir -p /home/user/.local/share/keyrings && \
printf '[keyring]\ndisplay-name=Default keyring\nctime=1732780800\nmtime=1732780800\nlock-on-idle=false\nlock-after=false\n' > /home/user/.local/share/keyrings/Default_keyring.keyring && \
echo 'Default_keyring' > /home/user/.local/share/keyrings/default && \
chmod 600 /home/user/.local/share/keyrings/Default_keyring.keyring && \
chmod 600 /home/user/.local/share/keyrings/default && \
# 创建基本的用户 .bashrc (只包含别名,不包含输入法配置)
printf '# User aliases\nalias chromium="/usr/local/bin/chromium"\nalias chrome="/usr/local/bin/chromium"\nalias code="code --no-sandbox"\n' > /home/user/.bashrc && \
# 设置权限安全配置755 允许读取和执行,所有者可写)
chown -R user:user /home/user /run/user/$(id -u user) && \
chmod 755 /home/user && \
chmod 700 /run/user/$(id -u user) && \
chown -R 1000:1000 /home/user/.config && \
chown -R 1000:1000 /home/user/.local && \
# Clean up package cache
apt-get autoremove -y && \
apt-get autoclean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Copy Chromium browser launcher script with fcitx support (runs as root, HOME=/home/user)
# IMPORTANT: Replace /usr/bin/chromium with our launcher so XFCE's exo-open uses it
COPY chromium-browser-launcher.sh /usr/bin/chromium-browser-launcher
RUN chmod 755 /usr/bin/chromium-browser-launcher && \
# Backup original chromium wrapper
mv /usr/bin/chromium /usr/bin/chromium-bin && \
# Replace /usr/bin/chromium with our launcher (XFCE uses this path)
cp /usr/bin/chromium-browser-launcher /usr/bin/chromium && \
ln -sf /usr/bin/chromium-browser-launcher /usr/local/bin/chromium
# Copy MCP-specific Chromium wrapper script (runs as user to connect to fcitx5)
COPY chromium-for-mcp.sh /usr/local/bin/chromium-for-mcp
RUN chmod 755 /usr/local/bin/chromium-for-mcp
# Create a socat service with supervisor to ensure port forwarding is always running
# RUN mkdir -p /etc/supervisor/conf.d && \
# echo '[supervisord]\nnodaemon=true\n\n[program:socat]\ncommand=socat TCP4-LISTEN:9223,fork,reuseaddr TCP4:127.0.0.1:9222\nautostart=true\nautorestart=true\nstdout_logfile=/var/log/socat.log\nstdout_logfile_maxbytes=1MB\nstdout_logfile_backups=10\nstderr_logfile=/var/log/socat.err\nstderr_logfile_maxbytes=1MB\nstderr_logfile_backups=10' > /etc/supervisor/conf.d/socat.conf
# Start the socat service at system boot
# RUN echo '#!/bin/bash\n# Start supervisor to manage socat service\n/usr/bin/supervisord -c /etc/supervisor/supervisord.conf &' > /usr/local/bin/start-port-forward.sh && \
# chmod +x /usr/local/bin/start-port-forward.sh && \
# echo "[Desktop Entry]\nType=Application\nName=PortForward\nExec=/usr/local/bin/start-port-forward.sh\nHidden=false\nNoDisplay=false\nX-GNOME-Autostart-enabled=true" > /home/user/.config/autostart/port-forward.desktop
# 添加自动启动supervisord的桌面启动项 (使用系统级配置)
RUN echo '#!/bin/bash\n# 启动supervisord服务\nsudo /usr/bin/supervisord -c /etc/supervisor/supervisord.conf &' > /usr/local/bin/start-supervisord.sh && \
chmod +x /usr/local/bin/start-supervisord.sh && \
echo "[Desktop Entry]\nType=Application\nName=Supervisord\nExec=/usr/local/bin/start-supervisord.sh\nHidden=false\nNoDisplay=false\nX-GNOME-Autostart-enabled=true" > /etc/xdg/autostart/supervisord.desktop && \
chmod 644 /etc/xdg/autostart/supervisord.desktop
# Install VS Code - 使用 apt 仓库方式(比直接下载 deb 更可靠)
RUN echo "📦 Adding Microsoft VS Code repository..." && \
# 添加微软 GPG 密钥
curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-archive-keyring.gpg && \
# 添加 apt 仓库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list && \
echo "📥 Installing VS Code via apt..." && \
apt-get update && \
apt-get install -y code && \
echo "🔍 Checking installed VS Code..." && \
which code && \
ls -la /usr/bin/code && \
echo "✅ VS Code installed via apt!" && \
# Cleanup
apt-get autoremove -y && \
apt-get autoclean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# 验证关键软件安装是否成功
RUN echo "🔍 Verifying installations..." && \
# VS Code - 检查多个可能的路径
CODE_PATH="" && \
if [ -x /usr/bin/code ]; then CODE_PATH="/usr/bin/code"; \
elif [ -x /usr/share/code/bin/code ]; then CODE_PATH="/usr/share/code/bin/code"; \
elif command -v code >/dev/null 2>&1; then CODE_PATH="$(command -v code)"; \
fi && \
if [ -z "$CODE_PATH" ]; then \
echo "❌ VS Code installation failed! No 'code' binary found" && \
echo "📂 Searching for code binary..." && \
find /usr -name "code" -type f 2>/dev/null | head -10 && \
exit 1; \
fi && \
echo "✅ VS Code found at: $CODE_PATH" && \
# 如果 /usr/bin/code 不存在,创建软链接
if [ ! -x /usr/bin/code ] && [ -n "$CODE_PATH" ]; then \
ln -sf "$CODE_PATH" /usr/bin/code && \
echo "🔗 Created symlink: /usr/bin/code -> $CODE_PATH"; \
fi && \
# Chromium
if [ ! -x /usr/bin/chromium ]; then \
echo "❌ Chromium installation failed!" && exit 1; \
fi && \
echo "✅ Chromium: $(/usr/bin/chromium --version 2>/dev/null || echo 'installed')" && \
echo "🎉 All critical software verified!"
RUN mkdir -p /home/user/.config/Code/User
COPY ./settings.json /home/user/.config/Code/User/settings.json
# Copy desktop background for XFCE
COPY ./wallpaper.jpeg /usr/share/backgrounds/xfce/wallpaper.jpeg
# Copy xfce4-desktop.xml to system-level config
RUN mkdir -p /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/
COPY ./xfce4-desktop.xml /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-desktop.xml
# ========== 系统级 XFCE 配置 (不受用户目录挂载影响) ==========
RUN mkdir -p /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/ && \
# Disable screen saver
echo '<?xml version="1.0" encoding="UTF-8"?>\n<channel name="xfce4-screensaver" version="1.0">\n <property name="saver" type="empty">\n <property name="enabled" type="bool" value="false"/>\n <property name="mode" type="int" value="0"/>\n </property>\n <property name="lock" type="empty">\n <property name="enabled" type="bool" value="false"/>\n </property>\n</channel>' > /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-screensaver.xml && \
# Disable power management screen dimming
echo '<?xml version="1.0" encoding="UTF-8"?>\n<channel name="xfce4-power-manager" version="1.0">\n <property name="xfce4-power-manager" type="empty">\n <property name="blank-on-ac" type="int" value="0"/>\n <property name="blank-on-battery" type="int" value="0"/>\n <property name="dpms-enabled" type="bool" value="false"/>\n <property name="dpms-on-ac-sleep" type="uint" value="0"/>\n <property name="dpms-on-ac-off" type="uint" value="0"/>\n <property name="dpms-on-battery-sleep" type="uint" value="0"/>\n <property name="dpms-on-battery-off" type="uint" value="0"/>\n </property>\n</channel>' > /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml && \
# Configure XFCE panel with top bar (panel-1) and bottom dock (panel-2)
echo '<?xml version="1.0" encoding="UTF-8"?>\n<channel name="xfce4-panel" version="1.0">\n <property name="configver" type="int" value="2"/>\n <property name="panels" type="array">\n <value type="int" value="1"/>\n <value type="int" value="2"/>\n <property name="dark-mode" type="bool" value="true"/>\n <property name="panel-1" type="empty">\n <property name="position" type="string" value="p=6;x=0;y=0"/>\n <property name="length" type="uint" value="100"/>\n <property name="position-locked" type="bool" value="true"/>\n <property name="icon-size" type="uint" value="16"/>\n <property name="size" type="uint" value="26"/>\n <property name="plugin-ids" type="array">\n <value type="int" value="1"/>\n <value type="int" value="2"/>\n <value type="int" value="3"/>\n <value type="int" value="4"/>\n <value type="int" value="5"/>\n <value type="int" value="6"/>\n <value type="int" value="7"/>\n <value type="int" value="8"/>\n </property>\n </property>\n <property name="panel-2" type="empty">\n <property name="autohide-behavior" type="uint" value="1"/>\n <property name="position" type="string" value="p=10;x=0;y=0"/>\n <property name="length" type="uint" value="1"/>\n <property name="position-locked" type="bool" value="true"/>\n <property name="size" type="uint" value="48"/>\n <property name="plugin-ids" type="array">\n <value type="int" value="15"/>\n <value type="int" value="16"/>\n <value type="int" value="17"/>\n <value type="int" value="18"/>\n <value type="int" value="19"/>\n <value type="int" value="20"/>\n <value type="int" value="21"/>\n </property>\n </property>\n </property>\n <property name="plugins" type="empty">\n <property name="plugin-1" type="string" value="applicationsmenu"/>\n <property name="plugin-2" type="string" value="tasklist">\n <property name="grouping" type="uint" value="1"/>\n </property>\n <property name="plugin-3" type="string" value="separator">\n <property name="expand" type="bool" value="true"/>\n <property name="style" type="uint" value="0"/>\n </property>\n <property name="plugin-4" type="string" value="systray">\n <property name="square-icons" type="bool" value="true"/>\n </property>\n <property name="plugin-5" type="string" value="separator">\n <property name="style" type="uint" value="0"/>\n </property>\n <property name="plugin-6" type="string" value="notification-plugin"/>\n <property name="plugin-7" type="string" value="clock"/>\n <property name="plugin-8" type="string" value="actions"/>\n <property name="plugin-15" type="string" value="showdesktop"/>\n <property name="plugin-16" type="string" value="separator"/>\n <property name="plugin-17" type="string" value="launcher">\n <property name="items" type="array">\n <value type="string" value="xfce4-terminal-emulator.desktop"/>\n </property>\n </property>\n <property name="plugin-18" type="string" value="launcher">\n <property name="items" type="array">\n <value type="string" value="xfce4-file-manager.desktop"/>\n </property>\n </property>\n <property name="plugin-19" type="string" value="launcher">\n <property name="items" type="array">\n <value type="string" value="chromium.desktop"/>\n </property>\n </property>\n <property name="plugin-20" type="string" value="launcher">\n <property name="items" type="array">\n <value type="string" value="xfce4-appfinder.desktop"/>\n </property>\n </property>\n <property name="plugin-21" type="string" value="directorymenu"/>\n </property>\n</channel>' > /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml && \
# Create launcher directories for bottom dock (系统级)
mkdir -p /etc/xdg/xfce4/panel/launcher-17 && \
mkdir -p /etc/xdg/xfce4/panel/launcher-18 && \
mkdir -p /etc/xdg/xfce4/panel/launcher-19 && \
mkdir -p /etc/xdg/xfce4/panel/launcher-20 && \
# Create terminal launcher (plugin-17)
printf '[Desktop Entry]\nVersion=1.0\nType=Application\nExec=xfce4-terminal\nIcon=org.xfce.terminalemulator\nStartupNotify=true\nTerminal=false\nCategories=Utility;X-XFCE;\nName=Terminal Emulator\nComment=Use the command line\n' > /etc/xdg/xfce4/panel/launcher-17/xfce4-terminal-emulator.desktop && \
# Create file manager launcher (plugin-18)
printf '[Desktop Entry]\nVersion=1.0\nType=Application\nExec=thunar\nIcon=org.xfce.filemanager\nStartupNotify=true\nTerminal=false\nCategories=Utility;X-XFCE;\nName=File Manager\nComment=Browse the file system\n' > /etc/xdg/xfce4/panel/launcher-18/xfce4-file-manager.desktop && \
# Create Chromium launcher (plugin-19) - replaces default Web Browser
printf '[Desktop Entry]\nVersion=1.0\nType=Application\nName=Chromium\nComment=Web Browser\nExec=/usr/bin/chromium-browser-launcher\nIcon=chromium\nTerminal=false\nCategories=Network;WebBrowser;\nStartupNotify=true\n' > /etc/xdg/xfce4/panel/launcher-19/chromium.desktop && \
# Create app finder launcher (plugin-20)
printf '[Desktop Entry]\nVersion=1.0\nType=Application\nExec=xfce4-appfinder\nIcon=org.xfce.appfinder\nStartupNotify=true\nTerminal=false\nCategories=Utility;X-XFCE;\nName=Application Finder\nComment=Find and launch applications\n' > /etc/xdg/xfce4/panel/launcher-20/xfce4-appfinder.desktop && \
# Create system-wide Chromium desktop entry
mkdir -p /usr/share/applications && \
printf "[Desktop Entry]\nVersion=1.0\nType=Application\nName=Chromium\nComment=Web Browser\nExec=/usr/bin/chromium-browser-launcher\nIcon=chromium\nTerminal=false\nCategories=Network;WebBrowser;" > /usr/share/applications/chromium-browser.desktop && \
ln -sf /usr/share/applications/chromium-browser.desktop /usr/share/applications/chromium.desktop && \
chmod 644 /usr/share/applications/chromium-browser.desktop /usr/share/applications/chromium.desktop && \
# Set Chromium as default web browser (系统级)
mkdir -p /usr/share/applications && \
echo "[Default Applications]\ntext/html=chromium.desktop\ntext/xml=chromium.desktop\napplication/xhtml+xml=chromium.desktop\napplication/xml=chromium.desktop\napplication/rss+xml=chromium.desktop\napplication/rdf+xml=chromium.desktop\nimage/gif=chromium.desktop\nimage/jpeg=chromium.desktop\nimage/png=chromium.desktop\nx-scheme-handler/http=chromium.desktop\nx-scheme-handler/https=chromium.desktop\nx-scheme-handler/ftp=chromium.desktop\nx-scheme-handler/chrome=chromium.desktop\nvideo/webm=chromium.desktop\napplication/x-xpinstall=chromium.desktop" > /usr/share/applications/mimeapps.list
# Create desktop icons for terminal, VS Code, and Chrome
RUN mkdir -p /home/user/Desktop /usr/share/applications && \
# Create safe desktop files in system directory (not in user's home)
printf "[Desktop Entry]\nVersion=1.0\nType=Application\nName=Terminal\nComment=Terminal Emulator\nExec=xfce4-terminal\nIcon=utilities-terminal\nTerminal=false\nCategories=System;TerminalEmulator;" > /usr/share/applications/user-terminal.desktop && \
printf "[Desktop Entry]\nVersion=1.0\nType=Application\nName=Visual Studio Code\nComment=Code Editor\nExec=/usr/bin/code --no-sandbox --user-data-dir=/home/user/.config/Code\nIcon=vscode\nTerminal=false\nCategories=Development;IDE;" > /usr/share/applications/user-vscode.desktop && \
chmod 644 /usr/share/applications/user-terminal.desktop /usr/share/applications/user-vscode.desktop && \
# Create symbolic links from Desktop to system apps (安全软链接)
ln -sf /usr/share/applications/user-terminal.desktop /home/user/Desktop/terminal.desktop && \
ln -sf /usr/share/applications/user-vscode.desktop /home/user/Desktop/vscode.desktop && \
ln -sf /usr/share/applications/chromium-browser.desktop /home/user/Desktop/chromium.desktop && \
chown -R user:user /home/user/Desktop
# ========== 创建骨架目录 /etc/skel-user-desktop ==========
# 将 /home/user 下的关键配置备份到此目录
# 启动时,如果检测到 /home/user 是空的(被挂载覆盖),则从这里恢复
# 这样可以解决:
# 1. 挂载空目录到 /home/user 导致桌面图标消失
# 2. 挂载空目录到 /home/user 导致 VNC 花屏(缺少关键缓存目录)
RUN mkdir -p /etc/skel-user-desktop && \
# 复制整个 /home/user 目录结构到骨架目录
cp -a /home/user/. /etc/skel-user-desktop/ && \
# 确保骨架目录只读(防止意外修改)
chmod 755 /etc/skel-user-desktop && \
echo "✅ Skeleton directory created at /etc/skel-user-desktop"
# Copy firefox policies
COPY firefox-policies.json /usr/lib/firefox-esr/distribution/policies.json
COPY firefox-autoconfig.js /usr/lib/firefox-esr/defaults/pref/autoconfig.js
COPY firefox.cfg /usr/lib/firefox-esr/firefox.cfg
# Code interpretor environment setup
# 系统依赖和Python 3安装 (merged for smaller image size)
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
apt-get install -y --no-install-recommends \
build-essential curl git util-linux jq sudo fonts-noto-cjk fonts-noto-color-emoji \
systemd ca-certificates chrony r-base software-properties-common \
libzmq3-dev libzmq5 pkg-config && \
# 使用 Debian 12 默认的 Python 3.11 (更稳定)
apt-get install -y python3 python3-dev python3-venv python3-pip && \
ln -sf /usr/bin/python3 /usr/bin/python && \
# Python/Jupyter services and kernels removed, but Python3 environment kept
# Clean up package cache to reduce image size:
apt-get autoremove -y && \
apt-get autoclean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# 安装 Node.js 22.x (现在支持,因为不再依赖 ijavascript)
RUN apt-get update && \
apt-get install -y curl gnupg ca-certificates && \
# 添加 NodeSource PPA for Node.js 22.x
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
# 安装 Node.js (npm 会一同被安装)
apt-get install -y nodejs && \
# 更新证书
update-ca-certificates && \
# Clean up package cache to reduce image size:
apt-get autoremove -y && \
apt-get autoclean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# 使用 npm 安装 pnpm使用阿里云镜像
RUN npm install -g pnpm@latest && \
# 创建 pnpm 环境配置脚本(用户通过终端使用时会加载)
echo '#!/bin/sh' > /etc/profile.d/pnpm-env.sh && \
echo '# PNPM environment configuration' >> /etc/profile.d/pnpm-env.sh && \
echo 'export PNPM_HOME="/home/user/.local/share/pnpm"' >> /etc/profile.d/pnpm-env.sh && \
echo 'export npm_config_registry="https://registry.npmmirror.com"' >> /etc/profile.d/pnpm-env.sh && \
chmod +x /etc/profile.d/pnpm-env.sh && \
# Clean up npm cache
npm cache clean --force && \
echo "Node.js version: $(node -v)" && \
echo "npm version: $(npm -v)" && \
echo "pnpm version: $(pnpm -v)"
# 环境变量设置
ENV R_VERSION=4.4.2
ENV R_HOME=/opt/R/${R_VERSION}
# # 创建默认用户
# RUN useradd -ms /bin/bash user && \
# usermod -aG sudo user && \
# passwd -d user && \
# echo "user ALL=(ALL:ALL) NOPASSWD: ALL" >>/etc/sudoers
# 设置目录权限(安全配置)
RUN mkdir -p /home/user && \
chown -R user:user /home/user && \
chmod 755 /home/user && \
chmod 755 /usr/local
# Python/Jupyter components removed
# JavaScript/TypeScript 使用 Deno kernel (不需要安装 ijavascript)
# Deno kernel 在上面已经安装并配置完成
# Deno runtime (for JavaScript/TypeScript support)
COPY --from=denoland/deno:bin-2.5.6 /deno /usr/bin/deno
RUN chmod +x /usr/bin/deno && \
# Clean up deno cache
rm -rf /root/.cache/deno
# Note: deno.json not copied as Jupyter is removed
# Bash Kernel (Python-based) removed
# R Kernel
# RUN R -e "install.packages('IRkernel', repos='https://cloud.r-project.org'); IRkernel::installspec(user = FALSE, name = 'r', displayname = 'R')"
# envd 服务已删除 - 不再需要环境守护进程
# 配置 chrony
RUN mkdir -p /etc/chrony
RUN echo 'makestep 1 -1' > /etc/chrony/chrony.conf
RUN mkdir -p /etc/systemd/system/chrony.service.d
RUN echo '[Service]\n\
ExecStart=\n\
ExecStart=/usr/sbin/chronyd\n\
User=root\n\
Group=root' > /etc/systemd/system/chrony.service.d/override.conf
RUN systemctl enable chrony
# Java 配置 (完整 JDK 工具集)
RUN apt-get update && \
apt-get install -y openjdk-17-jdk && \
# 验证安装
java -version && \
javac -version && \
echo "✅ OpenJDK 17 installed with full toolset" && \
# 确认 javac 存在
which javac && \
ls -la /usr/lib/jvm/java-17-openjdk-*/bin/javac && \
# 清理缓存(但不 autoremove避免删除 JDK 依赖)
rm -rf /var/lib/apt/lists/*
# ============================================================================
# 📦 Maven 配置(使用阿里云镜像加速)
# ============================================================================
# 安装 Maven
RUN apt-get update && apt-get install -y maven && \
rm -rf /var/lib/apt/lists/* && \
echo "✅ Maven installed: $(mvn --version | head -1)"
# 创建 Maven 配置目录(系统级和用户级)
RUN mkdir -p /home/user/.m2 && \
mkdir -p /etc/maven
# 复制 Maven 配置文件(阿里云镜像已配置)
COPY settings.xml /home/user/.m2/settings.xml
COPY settings.xml /etc/maven/settings.xml
# 设置权限
RUN chown -R user:user /home/user/.m2 && \
chmod 644 /home/user/.m2/settings.xml && \
echo "✅ Maven configured with Aliyun mirror"
# 配置自动登录
RUN mkdir -p /etc/systemd/system/serial-getty@ttyS0.service.d
RUN echo '[Service]\n\
ExecStart=\n\
ExecStart=-/sbin/agetty --noissue --autologin root %I 115200,38400,9600 vt102' \
> /etc/systemd/system/serial-getty@ttyS0.service.d/autologin.conf
# Python server components removed
# IPython/Jupyter config files removed
ENV DISPLAY=:0
# 设置全局输入法环境变量(容器级别 - 纯 fcitx
ENV GTK_IM_MODULE=fcitx \
QT_IM_MODULE=fcitx \
XMODIFIERS=@im=fcitx \
INPUT_METHOD=fcitx \
SDL_IM_MODULE=fcitx \
GLFW_IM_MODULE=ibus
# 配置 D-Bus 和 PolicyKit已在前面安装 dbus-x11
RUN mkdir -p /etc/dbus-1/session.d && \
printf '<!-- Session bus for user -->\n<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">\n<busconfig>\n <policy user="user">\n <allow own="*"/>\n <allow own_prefix="*"/>\n <allow send_destination="*"/>\n <allow receive_sender="*"/>\n </policy>\n</busconfig>' > /etc/dbus-1/session.d/user-session.conf && \
printf '<!-- Allow root to access user session bus for fcitx5 input method -->\n<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">\n<busconfig>\n <policy user="root">\n <allow own="*"/>\n <allow own_prefix="*"/>\n <allow send_destination="*"/>\n <allow receive_sender="*"/>\n <allow eavesdrop="true"/>\n </policy>\n</busconfig>' > /etc/dbus-1/session.d/allow-root.conf && \
# Set proper permissions
chown -R root:root /etc/polkit-1 /etc/dbus-1 && \
chmod 755 /etc/polkit-1/localauthority/50-local.d /etc/dbus-1/session.d && \
chmod 644 /etc/polkit-1/localauthority/50-local.d/*.pkla /etc/dbus-1/session.d/*.conf
# 安装 agent_runner 运行时依赖(与 rcoder/Dockerfile-runner 保持一致)
RUN apt-get update && apt-get install -y \
ca-certificates \
curl \
wget \
gnupg \
unzip \
zip \
tzdata \
lsof \
iproute2 \
net-tools \
nginx \
&& rm -rf /var/lib/apt/lists/*
# 设置系统时区为东八区Asia/Shanghai
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo "Asia/Shanghai" > /etc/timezone
# 清理默认nginx配置以避免冲突
RUN rm -f /etc/nginx/sites-enabled/default && \
rm -f /etc/nginx/conf.d/default.conf
# 配置 npm 使用国内镜像源(系统级配置)
RUN npm config set registry https://registry.npmmirror.com && \
# 创建 npm 环境配置脚本(用户通过终端使用时会加载)
echo '#!/bin/sh' > /etc/profile.d/npm-env.sh && \
echo '# NPM environment configuration' >> /etc/profile.d/npm-env.sh && \
echo 'export npm_config_registry=https://registry.npmmirror.com' >> /etc/profile.d/npm-env.sh && \
chmod +x /etc/profile.d/npm-env.sh
# 设置工作目录并确保权限
RUN mkdir -p /app/computer-project-workspace /home/user/.cache && \
chown -R user:user /app /home/user
# 安装 Bun 到全局路径(需要 root
ENV BUN_CONFIG_REGISTRY="https://registry.npmmirror.com" \
BUN_CONFIG_INSTALL_CACHE_DIR="/home/user/.cache/bun/install" \
BUN_CONFIG_GLOBAL_CACHE_DIR="/home/user/.cache/bun/global" \
BUN_INSTALL="/usr/local" \
PATH="/usr/local/bin:$PATH"
# 使用 npm 从阿里云 npmmirror 安装 bun 的 prebuilt 二进制,避免从 bun.sh/install 走 GitHub 下载不稳定
# bun 的 npm 包通过 optionalDependencies 引入平台特定二进制(如 @oven/bun-linux-x64npmmirror 已镜像
# --prefix=/usr/local 让 bun 安装到 /usr/local/bin/bun与上方 BUN_INSTALL 路径保持一致)
RUN npm install -g --prefix=/usr/local --registry=https://registry.npmmirror.com bun && \
/usr/local/bin/bun --version && \
# 创建 bun 环境配置脚本(用户通过终端使用时会加载)
echo '#!/bin/sh' > /etc/profile.d/bun-env.sh && \
echo '# Bun environment configuration' >> /etc/profile.d/bun-env.sh && \
echo 'export BUN_CONFIG_REGISTRY="https://registry.npmmirror.com"' >> /etc/profile.d/bun-env.sh && \
echo 'export BUN_CONFIG_INSTALL_CACHE_DIR="/home/user/.cache/bun/install"' >> /etc/profile.d/bun-env.sh && \
echo 'export BUN_CONFIG_GLOBAL_CACHE_DIR="/home/user/.cache/bun/global"' >> /etc/profile.d/bun-env.sh && \
echo 'export BUN_INSTALL="/usr/local"' >> /etc/profile.d/bun-env.sh && \
chmod +x /etc/profile.d/bun-env.sh && \
# Clean up npm cache
npm cache clean --force
# 安装全局 npm 包(需要 root因为安装到 /usr/lib/node_modules
RUN npm install -g @zed-industries/claude-code-acp@latest && \
npm install -g @anthropic-ai/claude-code@latest && \
npm install -g chrome-devtools-mcp@latest && \
# 安装 pptxgenjs 和 sharp全局安装不受 /home/user 挂载影响)
npm install -g pptxgenjs sharp && \
# Clean up npm cache
npm cache clean --force && \
echo "✅ pptxgenjs and sharp installed globally"
# # 安装 Playwright Chromium独立 RUN 以利用 Docker 缓存)
# RUN npx playwright install chromium && \
# echo "✅ Playwright Chromium installed"
# 安装 OpenSSL 开发包和构建依赖mcp-stdio-proxy 需要)
RUN apt-get update && apt-get install -y \
libssl-dev \
pkg-config \
build-essential && \
rm -rf /var/lib/apt/lists/*
# 安装 Rust toolchain需要 root
# 注意mcp-stdio-proxy 的安装已移至 Dockerfile以便频繁更新时无需重建基础镜像
ENV RUSTUP_HOME=/opt/rustup \
CARGO_HOME=/opt/cargo \
PATH="/opt/cargo/bin:$PATH"
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \
chmod -R 755 /opt/rustup /opt/cargo && \
# 确保 user 可以使用 cargo
chmod -R o+rX /opt/cargo /opt/rustup
# 安装 uv 到全局路径(需要 root
# 注意UV_CACHE_DIR 在后续会根据 root/user 模式分别设置,这里不设置全局默认
# 使用阿里云 PyPI 镜像源(与 pip 保持一致,稳定可靠,无限流限制)
# 使用 pip 安装(避免从 astral.sh/GitHub 下载二进制慢/不稳定pip 全局安装默认放 /usr/local/bin
ENV UV_INDEX_URL=https://mirrors.aliyun.com/pypi/simple
RUN pip3 install --break-system-packages --no-cache-dir -i https://mirrors.aliyun.com/pypi/simple uv && \
uv --version && \
# 创建 uv 环境配置脚本(用户通过终端使用时会加载)
echo '#!/bin/sh' > /etc/profile.d/uv-env.sh && \
echo '# UV environment configuration' >> /etc/profile.d/uv-env.sh && \
echo 'export UV_INDEX_URL=https://mirrors.aliyun.com/pypi/simple' >> /etc/profile.d/uv-env.sh && \
echo 'export UV_TOOL_DIR=/home/user/.local/share/uv/tools' >> /etc/profile.d/uv-env.sh && \
echo 'export UV_CACHE_DIR=/home/user/.cache/uv' >> /etc/profile.d/uv-env.sh && \
chmod +x /etc/profile.d/uv-env.sh
# 添加 cargo bin 和 uv 到 PATH
# 添加所有工具安装路径到 PATH:
# - /root/.local/bin: root 用户使用 uv tool install 时的可执行文件位置
# - /home/user/.local/bin: user 用户使用 uv tool install 时的可执行文件位置
# - /opt/cargo/bin: Rust cargo 安装的工具
# - /usr/local/bin: 系统级工具bun, uv, pnpm 等)
ENV PNPM_HOME="/home/user/.local/share/pnpm" \
PATH="/root/.local/bin:/home/user/.local/bin:/opt/cargo/bin:/usr/local/bin:${PATH}"
# ============================================================================
# 🔊 pcmflux 音频流服务安装
# ============================================================================
# 安装 pcmflux Python 包(需要 root用于编译 C++ 扩展)
# 使用 --break-system-packages 绕过 Debian 12 的 PEP 668 限制
RUN pip3 install --break-system-packages websockets && \
if [ "${USE_GITHUB_MIRROR}" = "true" ]; then \
pip3 install --break-system-packages git+${GITHUB_MIRROR_URL}linuxserver/pcmflux.git; \
else \
pip3 install --break-system-packages git+https://github.com/linuxserver/pcmflux.git; \
fi && \
echo "✅ pcmflux audio streaming installed"
# 暴露音频流端口
EXPOSE 6090
EXPOSE 6089
# 暴露 IME 服务端口
EXPOSE 6091
# ============================================================================
# 预安装全局工具root 模式)- 系统级别,不受 /home/user 挂载影响
# ============================================================================
# 预安装 context7 MCP 工具Bun 全局安装)
# 代码里通过 bunx 调用 MCP预装到 bun 全局后 bunx 可直接命中 ~/.bun/install/global无需运行时再下载
RUN bun add -g @upstash/context7-mcp
# 预安装 mcp-server-fetchUV 全局安装到系统目录)
ENV UV_TOOL_DIR=/usr/local/share/uv/tools \
UV_CACHE_DIR=/var/cache/uv
RUN mkdir -p "$UV_TOOL_DIR" "$UV_CACHE_DIR" && \
uv tool install mcp-server-fetch && \
echo "✅ mcp-server-fetch installed to $UV_TOOL_DIR"
# ============================================================================
# 🎯 切换到普通用户 - 后续操作以 user 身份执行
# ============================================================================
USER user
WORKDIR /home/user
# 配置 pnpm用户级配置
RUN pnpm config set registry https://registry.npmmirror.com && \
pnpm config set store-dir /home/user/.cache/pnpm
# 配置 Bun 用户配置文件
RUN mkdir -p /home/user/.cache/bun/install /home/user/.cache/bun/global && \
echo '[install]' > /home/user/.bunfig.toml && \
echo 'registry = "https://registry.npmmirror.com"' >> /home/user/.bunfig.toml && \
echo '' >> /home/user/.bunfig.toml && \
echo '[install.cache]' >> /home/user/.bunfig.toml && \
echo 'dir = "/home/user/.cache/bun"' >> /home/user/.bunfig.toml && \
cat /home/user/.bunfig.toml && \
echo "✅ Bun configured for user"
# 配置 UV 用户环境(用户自己安装的工具会保存在这里,挂载后保留)
ENV UV_TOOL_DIR=/home/user/.local/share/uv/tools \
UV_CACHE_DIR=/home/user/.cache/uv
RUN mkdir -p "$UV_TOOL_DIR" "$UV_CACHE_DIR" && \
echo "✅ UV configured for user: tools in $UV_TOOL_DIR, cache in $UV_CACHE_DIR"
# 创建 Claude Code 配置目录
RUN mkdir -p /home/user/.claude && \
echo '{"permissions":{"deny":["WebFetch", "WebSearch"]}}' > /home/user/.claude/settings.json
# ============================================================================
# 🎯 切回 root - 更新骨架目录
# ============================================================================
USER root
# 更新骨架目录(包含 user 阶段创建的配置)
# 这样当宿主机挂载空目录到 /home/user 时start-up.sh 可以从这里恢复
RUN echo "📦 Updating skeleton directory with user configs..." && \
cp -a /home/user/. /etc/skel-user-desktop/ && \
echo "✅ Skeleton directory updated at /etc/skel-user-desktop"
# ============================================================================
# 基础镜像构建完成
# ============================================================================
# 此处不设置 ENTRYPOINT由最终镜像设置
# 暴露 agent_runner 端口(供最终镜像使用)
EXPOSE 8086
# 设置 agent_runner 默认端口环境变量
ENV AGENT_RUNNER_PORT=8086
# 设置默认工作目录
WORKDIR /home/user