Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e9fab03
feat(i18n): Add Vue i18n support for English and Chinese UI
LimLLL Apr 22, 2026
c0b7406
feat(config): Add config.toml editor modal and backend routes
LimLLL Apr 22, 2026
036708f
feat(composer): Add MCP server toggle dropdown
LimLLL Apr 22, 2026
74f3476
fix(composer): Handle dictation auth and transcription errors
LimLLL Apr 22, 2026
7d1a002
feat(file-manager): Add file manager UI and server routes
LimLLL Apr 22, 2026
ca97dc8
feat(file-manager): Add file preview modal and preview API endpoints
LimLLL Apr 22, 2026
d74a27a
feat(docker): Add Docker packaging and GHCR publish workflow
LimLLL Apr 22, 2026
aabeb0a
fix(ci): Use normalized image name across Docker workflow
LimLLL Apr 22, 2026
27e3e02
improve(ui): Enhance dropdown interactivity and dark mode styling
LimLLL Apr 22, 2026
b8dbdb3
fix(docker): Include pnpm lockfile for reproducible image builds
LimLLL Apr 22, 2026
8d07246
fix(docker): Use lockfile for production dependency installation
LimLLL Apr 22, 2026
0751a51
Merge remote-tracking branch 'upstream/main'
LimLLL Apr 22, 2026
d5ea9b1
improve(ui): Localize terminal and context UI text
LimLLL Apr 22, 2026
510f771
feat(ui): Add clean and developer conversation display modes
LimLLL Apr 22, 2026
d8c633a
fix(composer): Ignore Enter while IME composition is active
LimLLL Apr 22, 2026
4c8ab82
fix(docker): Rebuild node-pty during Docker image installation
LimLLL Apr 22, 2026
aeba039
chore(pnpm): Configure onlyBuiltDependencies for native builds
LimLLL Apr 22, 2026
823162f
improve(docker): Enhance container runtime setup and tool availability
LimLLL Apr 22, 2026
2ace4e2
feat(config): Add configurable Web UI password support
LimLLL Apr 22, 2026
cfeca44
feat(cli): Allow password to be set via environment variable
LimLLL Apr 22, 2026
f9322f5
chore(docker): Add ripgrep to Docker image dependencies
LimLLL Apr 23, 2026
2143dc3
improve(file-manager): Add icons for Office and spreadsheet files
LimLLL Apr 23, 2026
5b8269c
fix(thread): Recover MCP tool calls and timing from session logs
LimLLL Apr 23, 2026
20a2363
Merge remote-tracking branch 'upstream/main'
LimLLL Apr 23, 2026
74098bf
test: Add Vitest dependency to lockfile
LimLLL Apr 23, 2026
3788b48
refactor(i18n): Switch locale keys to English text fallback
LimLLL Apr 23, 2026
85c123c
fix(ui): Preserve and reconcile live MCP tool calls by turn
LimLLL Apr 23, 2026
89c097f
fix(docker): Move ripgrep installation to the runtime image
LimLLL Apr 23, 2026
e0c8c32
chore(config): Document SHELL setting in env example
LimLLL Apr 23, 2026
7d54ac9
chore(config): add missed environment variable to env example file
LimLLL Apr 23, 2026
c6b05b3
feat(content): Open linked files in preview modal from conversation
LimLLL Apr 23, 2026
97bd87f
docs: Update README with Docker setup and feature highlights
LimLLL Apr 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules
dist
dist-cli
.git
.idea
.claude
*.md
!README.md
50 changes: 50 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# ── Required ──────────────────────────────────────────────
# OpenAI API key for Codex
OPENAI_API_KEY=sk-xxx

# Terminal shell path
# Docker containers typically need /bin/bash; macOS defaults to /bin/zsh
SHELL=/bin/bash

# ── Optional ─────────────────────────────────────────────
# Codex config/session storage (default: ~/.codex)
# CODEX_HOME=/root/.codex

# Default workspace directory for file operations
# CODEX_WORKSPACE=/workspace

# Server port (default: 5900)
# CODEXUI_SERVER_PORT=5900

# Web UI password (if unset, a random password is generated on each start)
# CODEXUI_PASSWORD=your-password-here

# Sandbox mode: read-only | workspace-write | danger-full-access
# CODEXUI_SANDBOX_MODE=workspace-write

# Approval policy for tool calls: suggest | auto-edit | never
# CODEXUI_APPROVAL_POLICY=suggest

# Custom Codex CLI binary path (default: auto-detected)
# CODEXUI_CODEX_COMMAND=/usr/local/bin/codex

# Custom ripgrep binary path (default: auto-detected)
# CODEXUI_RG_COMMAND=/usr/local/bin/rg

# OfficeCLI binary path (default: officecli in PATH)
# OFFICECLI_PATH=/usr/local/bin/officecli

# ── Performance Logging (optional) ──────────────────────
# Enable API performance logging
# CODEXUI_API_PERF_LOGGING=true

# Log requests slower than this threshold in ms (default: 300)
# CODEXUI_API_PERF_MS_THRESHOLD=300

# Log responses larger than this threshold in MB (default: 1)
# CODEXUI_API_PERF_BODY_MB_THRESHOLD=1

# ── Telegram Integration (optional) ─────────────────────
# TELEGRAM_BOT_TOKEN=
# TELEGRAM_ALLOWED_USER_IDS=
# TELEGRAM_DEFAULT_CWD=/workspace
144 changes: 144 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
name: Build & Push Docker Image to GHCR

on:
push:
tags:
- 'v*'
workflow_dispatch:

env:
REGISTRY: ghcr.io

jobs:
build-and-push-arch:
strategy:
max-parallel: 2
matrix:
include:
- arch: amd64
runner: ubuntu-latest
- arch: arm64
runner: ubuntu-22.04-arm

runs-on: ${{ matrix.runner }}

permissions:
contents: read
packages: write

outputs:
version: ${{ steps.meta.outputs.version }}
image: ${{ steps.meta.outputs.image }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Get version tag and image name
id: meta
run: |
if [[ "${{ github.ref_type }}" == "tag" ]]; then
VERSION="${{ github.ref_name }}"
else
VERSION="dev-$(git rev-parse --short HEAD)"
fi
IMAGE_NAME=$(echo "${{ env.REGISTRY }}/${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "image=$IMAGE_NAME" >> $GITHUB_OUTPUT
echo "Version: $VERSION, Image: $IMAGE_NAME"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push (${{ matrix.arch }})
id: buildx1
continue-on-error: true
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/${{ matrix.arch }}
tags: |
${{ steps.meta.outputs.image }}:${{ matrix.arch }}-${{ steps.meta.outputs.version }}
push: true
provenance: false
sbom: false
cache-from: type=gha,scope=${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}

- name: Retry build on failure
if: steps.buildx1.outcome != 'success'
run: sleep 10

- name: Build and push (retry)
if: steps.buildx1.outcome != 'success'
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/${{ matrix.arch }}
tags: |
${{ steps.meta.outputs.image }}:${{ matrix.arch }}-${{ steps.meta.outputs.version }}
push: true
provenance: false
sbom: false
cache-from: type=gha,scope=${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}

merge-manifest:
needs: build-and-push-arch
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create and push manifest lists
run: |
VERSION="${{ needs.build-and-push-arch.outputs.version }}"
IMAGE_BASE="${{ needs.build-and-push-arch.outputs.image }}"

LATEST_TAG="${IMAGE_BASE}:latest"
VERSION_TAG="${IMAGE_BASE}:${VERSION}"

echo "Creating manifest for ${VERSION_TAG} and ${LATEST_TAG}..."

# Clean up old manifests
docker manifest rm ${LATEST_TAG} 2>/dev/null || true
docker manifest rm ${VERSION_TAG} 2>/dev/null || true

# Version tag manifest
docker manifest create ${VERSION_TAG} \
--amend ${IMAGE_BASE}:amd64-${VERSION} \
--amend ${IMAGE_BASE}:arm64-${VERSION}
docker manifest push ${VERSION_TAG}

# Latest tag manifest
docker manifest create ${LATEST_TAG} \
--amend ${IMAGE_BASE}:amd64-${VERSION} \
--amend ${IMAGE_BASE}:arm64-${VERSION}
docker manifest push ${LATEST_TAG}

echo "Manifests pushed successfully"

- name: Inspect final manifests
run: |
IMAGE_BASE="${{ needs.build-and-push-arch.outputs.image }}"
echo "--- Version Manifest ---"
docker manifest inspect ${IMAGE_BASE}:${{ needs.build-and-push-arch.outputs.version }}
echo "--- Latest Manifest ---"
docker manifest inspect ${IMAGE_BASE}:latest
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ output/
*.tsbuildinfo

package-lock.json
pnpm-lock.yaml
.codex/
.env.local

package.json
DYNAMIC_INSTRUCTIONS.md
.omx/
.idea
83 changes: 83 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
FROM node:20-bookworm-slim AS builder

WORKDIR /src

RUN apt-get update && apt-get install -y --no-install-recommends \
git \
ca-certificates \
python3 \
make \
g++ \
&& rm -rf /var/lib/apt/lists/*

RUN corepack enable

COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile && pnpm rebuild node-pty

COPY . .
RUN NODE_ENV=production pnpm run build


FROM node:20-bookworm-slim

WORKDIR /app

# Common system packages
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
git \
curl \
wget \
tini \
jq \
openssh-client \
procps \
libicu72 \
python3 \
make \
g++ \
ripgrep \
&& rm -rf /var/lib/apt/lists/*

RUN corepack enable

# Install mise (runtime version manager) + activate in shell
RUN curl -fsSL https://mise.run | sh && \
ln -s /root/.local/bin/mise /usr/local/bin/mise && \
echo 'eval "$(mise activate bash)"' >> /root/.bashrc && \
echo 'export PATH="/root/.local/share/mise/shims:$PATH"' >> /root/.bashrc

# Make mise shims available in non-interactive shells (for codex subprocess)
ENV PATH="/root/.local/share/mise/shims:/root/.local/bin:${PATH}"

# Install codex CLI
RUN npm install -g @openai/codex

# Install OfficeCLI (office file preview)
RUN ARCH=$(uname -m) && \
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then \
BINARY="officecli-linux-arm64"; \
else \
BINARY="officecli-linux-x64"; \
fi && \
curl -fsSL "https://github.com/iOfficeAI/OfficeCLI/releases/latest/download/${BINARY}" -o /usr/local/bin/officecli && \
chmod +x /usr/local/bin/officecli

# Install runtime dependencies for node-pty
COPY --from=builder /src/package.json /src/pnpm-lock.yaml /app/
RUN pnpm install --prod --frozen-lockfile && pnpm rebuild node-pty

# Copy build artifacts
COPY --from=builder /src/dist /app/dist
COPY --from=builder /src/dist-cli /app/dist-cli

COPY entrypoint.sh /app/entrypoint.sh

ENV NODE_ENV=production
ENV CODEX_HOME=/root/.codex

EXPOSE 5900

ENTRYPOINT ["/usr/bin/tini", "--", "/app/entrypoint.sh"]
CMD ["node", "dist-cli/index.js", "--port", "5900", "--no-open", "--no-login", "--no-tunnel"]
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,32 @@ Notes:

---

## 🐳 Docker Deployment

```bash
# Copy and configure environment
cp .env.example .env
# Edit .env — set OPENAI_API_KEY and SHELL at minimum

# Start with docker-compose
docker-compose up -d

# Open in browser
# http://localhost:5900
```

Key environment variables (see `.env.example` for full list):

| Variable | Description | Default |
|---|---|---|
| `OPENAI_API_KEY` | OpenAI API key | *required* |
| `SHELL` | Terminal shell path | `/bin/zsh` |
| `CODEXUI_PASSWORD` | Web UI password | random on each start |
| `CODEX_WORKSPACE` | Default workspace directory | — |
| `CODEXUI_SANDBOX_MODE` | `read-only` / `workspace-write` / `danger-full-access` | — |

---

## ✨ Features
> **The payload.**

Expand All @@ -136,6 +162,9 @@ Notes:
- ⚡ No global install required for quick experimentation
- 🎙️ Built-in hold-to-dictate voice input with transcription to composer draft
- 🤖 Optional Telegram bot bridge: send messages to bot, forward into mapped thread, send assistant reply back to Telegram
- 🌐 i18n support with English/Chinese locale switching
- 📁 Built-in file manager with inline preview (images, PDF, video, audio, markdown, code, Office documents)
- 🐳 Docker support with multi-arch images publishing

### Telegram Bot Bridge (Optional)

Expand Down Expand Up @@ -175,6 +204,8 @@ Outgoing assistant messages are sent with Telegram `parse_mode=HTML` for formatt
## 🧩 Recent Product Features (from main commits)
> **Not just launch. Actual UX upgrades.**

- 📁 File manager with breadcrumbs, context menu, upload/download, and inline preview
- 🌐 English/Chinese i18n with auto-detection and settings toggle
- 🗂️ Searchable project picker in new-thread flow
- ➕ "Create Project" button next to "Select folder" with browser prompt
- 📌 New projects get pinned to top automatically
Expand Down Expand Up @@ -252,7 +283,7 @@ Outgoing assistant messages are sent with Telegram `parse_mode=HTML` for formatt
| Port already in use | Run on a free port or stop old process |
| `npx` fails | Update npm/node, then retry |
| Termux install fails | `pkg update && pkg upgrade` then reinstall `nodejs` |
| Cant open from other device | Check firewall, bind address, and LAN routing |
| Can't open from other device | Check firewall, bind address, and LAN routing |

---

Expand Down
26 changes: 26 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
services:
codexui:
image: ghcr.io/limlll/codexui:latest
# build: .
container_name: codexui
restart: unless-stopped
ports:
- "5900:5900"
volumes:
- codex-data:/root/.codex
- ./workspace:/workspace
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
- CODEX_HOME=/root/.codex
- CODEX_WORKSPACE=/workspace
- CODEXUI_PASSWORD=${CODEXUI_PASSWORD:-}
# - CODEXUI_SANDBOX_MODE=workspace-write
# - CODEXUI_APPROVAL_POLICY=suggest
# - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
# - TELEGRAM_ALLOWED_USER_IDS=${TELEGRAM_ALLOWED_USER_IDS}
# - TELEGRAM_DEFAULT_CWD=/workspace
env_file:
- .env

volumes:
codex-data:
Loading