diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..f06e4cca --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.git +.github +.paperclip +.pnpm-store +node_modules +**/node_modules +coverage +data +tmp +*.log diff --git a/Dockerfile b/Dockerfile index e46569a7..2339d2ff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,38 +1,46 @@ -FROM node:20-alpine AS base +FROM node:20-bookworm-slim AS base +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates curl git \ + && rm -rf /var/lib/apt/lists/* RUN corepack enable FROM base AS deps WORKDIR /app COPY package.json pnpm-workspace.yaml pnpm-lock.yaml .npmrc ./ -COPY packages/shared/package.json packages/shared/ -COPY packages/db/package.json packages/db/ +COPY cli/package.json cli/ COPY server/package.json server/ COPY ui/package.json ui/ +COPY packages/shared/package.json packages/shared/ +COPY packages/db/package.json packages/db/ +COPY packages/adapter-utils/package.json packages/adapter-utils/ +COPY packages/adapters/claude-local/package.json packages/adapters/claude-local/ +COPY packages/adapters/codex-local/package.json packages/adapters/codex-local/ RUN pnpm install --frozen-lockfile FROM base AS build WORKDIR /app -COPY --from=deps /app/node_modules node_modules -COPY --from=deps /app/packages/shared/node_modules packages/shared/node_modules -COPY --from=deps /app/packages/db/node_modules packages/db/node_modules -COPY --from=deps /app/server/node_modules server/node_modules -COPY --from=deps /app/ui/node_modules ui/node_modules +COPY --from=deps /app /app COPY . . RUN pnpm --filter @paperclip/ui build RUN pnpm --filter @paperclip/server build FROM base AS production WORKDIR /app -COPY --from=deps /app/node_modules node_modules -COPY --from=deps /app/packages/shared/node_modules packages/shared/node_modules -COPY --from=deps /app/packages/db/node_modules packages/db/node_modules -COPY --from=deps /app/server/node_modules server/node_modules -COPY --from=build /app/packages/shared packages/shared -COPY --from=build /app/packages/db packages/db -COPY --from=build /app/server/dist server/dist -COPY --from=build /app/server/package.json server/package.json -COPY --from=build /app/ui/dist ui/dist -COPY package.json pnpm-workspace.yaml ./ +COPY --from=build /app /app +RUN npm install --global --omit=dev @anthropic-ai/claude-code@latest @openai/codex@latest +ENV NODE_ENV=production \ + HOME=/paperclip \ + HOST=0.0.0.0 \ + PORT=3100 \ + SERVE_UI=true \ + PAPERCLIP_HOME=/paperclip \ + PAPERCLIP_INSTANCE_ID=default \ + PAPERCLIP_CONFIG=/paperclip/instances/default/config.json \ + PAPERCLIP_DEPLOYMENT_MODE=local_trusted \ + PAPERCLIP_DEPLOYMENT_EXPOSURE=private + +VOLUME ["/paperclip"] EXPOSE 3100 -CMD ["node", "server/dist/index.js"] + +CMD ["node", "--import", "./server/node_modules/tsx/dist/loader.mjs", "server/dist/index.js"] diff --git a/doc/DATABASE.md b/doc/DATABASE.md index 08673c4f..34640964 100644 --- a/doc/DATABASE.md +++ b/doc/DATABASE.md @@ -21,6 +21,8 @@ Data persists across restarts in `~/.paperclip/instances/default/db/`. To reset This mode is ideal for local development and one-command installs. +Docker note: the Docker quickstart image also uses embedded PostgreSQL by default. Persist `/paperclip` to keep DB state across container restarts (see `doc/DOCKER.md`). + ## 2. Local PostgreSQL (Docker) For a full PostgreSQL server locally, use the included Docker Compose setup: diff --git a/doc/DEVELOPING.md b/doc/DEVELOPING.md index 02488ec4..ad466160 100644 --- a/doc/DEVELOPING.md +++ b/doc/DEVELOPING.md @@ -57,6 +57,28 @@ pnpm paperclip run 2. `paperclip doctor` with repair enabled 3. starts the server when checks pass +## Docker Quickstart (No local Node install) + +Build and run Paperclip in Docker: + +```sh +docker build -t paperclip-local . +docker run --name paperclip \ + -p 3100:3100 \ + -e HOST=0.0.0.0 \ + -e PAPERCLIP_HOME=/paperclip \ + -v "$(pwd)/data/docker-paperclip:/paperclip" \ + paperclip-local +``` + +Or use Compose: + +```sh +docker compose -f docker-compose.quickstart.yml up --build +``` + +See `doc/DOCKER.md` for API key wiring (`OPENAI_API_KEY` / `ANTHROPIC_API_KEY`) and persistence details. + ## Database in Dev (Auto-Handled) For local development, leave `DATABASE_URL` unset. diff --git a/doc/DOCKER.md b/doc/DOCKER.md new file mode 100644 index 00000000..404a72e1 --- /dev/null +++ b/doc/DOCKER.md @@ -0,0 +1,68 @@ +# Docker Quickstart + +Run Paperclip in Docker without installing Node or pnpm locally. + +## One-liner (build + run) + +```sh +docker build -t paperclip-local . && \ +docker run --name paperclip \ + -p 3100:3100 \ + -e HOST=0.0.0.0 \ + -e PAPERCLIP_HOME=/paperclip \ + -v "$(pwd)/data/docker-paperclip:/paperclip" \ + paperclip-local +``` + +Open: `http://localhost:3100` + +Data persistence: + +- Embedded PostgreSQL data +- uploaded assets +- local secrets key +- local agent workspace data + +All persisted under your bind mount (`./data/docker-paperclip` in the example above). + +## Compose Quickstart + +```sh +docker compose -f docker-compose.quickstart.yml up --build +``` + +Defaults: + +- host port: `3100` +- persistent data dir: `./data/docker-paperclip` + +Optional overrides: + +```sh +PAPERCLIP_PORT=3200 PAPERCLIP_DATA_DIR=./data/pc docker compose -f docker-compose.quickstart.yml up --build +``` + +## Claude + Codex Local Adapters in Docker + +The image pre-installs: + +- `claude` (Anthropic Claude Code CLI) +- `codex` (OpenAI Codex CLI) + +If you want local adapter runs inside the container, pass API keys when starting the container: + +```sh +docker run --name paperclip \ + -p 3100:3100 \ + -e HOST=0.0.0.0 \ + -e PAPERCLIP_HOME=/paperclip \ + -e OPENAI_API_KEY=... \ + -e ANTHROPIC_API_KEY=... \ + -v "$(pwd)/data/docker-paperclip:/paperclip" \ + paperclip-local +``` + +Notes: + +- Without API keys, the app still runs normally. +- Adapter environment checks in Paperclip will surface missing auth/CLI prerequisites. diff --git a/docker-compose.quickstart.yml b/docker-compose.quickstart.yml new file mode 100644 index 00000000..373c5d48 --- /dev/null +++ b/docker-compose.quickstart.yml @@ -0,0 +1,14 @@ +services: + paperclip: + build: + context: . + dockerfile: Dockerfile + ports: + - "${PAPERCLIP_PORT:-3100}:3100" + environment: + HOST: "0.0.0.0" + PAPERCLIP_HOME: "/paperclip" + OPENAI_API_KEY: "${OPENAI_API_KEY:-}" + ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY:-}" + volumes: + - "${PAPERCLIP_DATA_DIR:-./data/docker-paperclip}:/paperclip"