Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
78 changes: 42 additions & 36 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,14 @@ WORKDIR /app
# =============================================================================
FROM base AS deps

# Copy only files needed for pnpm install (maximizes Docker layer cache)
# Copy root manifests, then use a bind-mount + find to copy every workspace
# package.json while preserving directory structure — no edits needed when
# packages or apps are added to the workspace.
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY packages/types/package.json packages/types/package.json
COPY packages/llm/package.json packages/llm/package.json
COPY packages/pdf/package.json packages/pdf/package.json
COPY packages/storage/package.json packages/storage/package.json
COPY packages/pipeline/package.json packages/pipeline/package.json
# COPY packages/output/package.json packages/output/package.json # uncomment when package is initialized
COPY apps/api/package.json apps/api/package.json
COPY apps/studio/package.json apps/studio/package.json
RUN --mount=type=bind,source=.,target=/ctx \
find /ctx/packages /ctx/apps -maxdepth 2 -name "package.json" \
-not -path "*/node_modules/*" \
-exec sh -c 'f="$1"; dst="${f#/ctx/}"; mkdir -p "$(dirname "$dst")"; cp "$f" "$dst"' _ {} \;

RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile
Expand All @@ -33,33 +31,15 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
# =============================================================================
FROM deps AS build

# Copy full source (preserve node_modules from deps stage)
# Copy full source (preserve node_modules from deps stage).
# Directory-level copies: adding a new package or app requires no Dockerfile changes.
# .dockerignore excludes node_modules/, dist/, apps/desktop/, and other build artifacts.
COPY tsconfig.json ./
COPY packages/ ./packages/
COPY apps/api/ ./apps/api/
COPY apps/studio/ ./apps/studio/

# Copy package sources (exclude node_modules to preserve installed deps)
COPY packages/types/src packages/types/src
COPY packages/types/tsconfig.json packages/types/tsconfig.json
COPY packages/llm/src packages/llm/src
COPY packages/llm/tsconfig.json packages/llm/tsconfig.json
COPY packages/pdf/src packages/pdf/src
COPY packages/pdf/tsconfig.json packages/pdf/tsconfig.json
COPY packages/storage/src packages/storage/src
COPY packages/storage/tsconfig.json packages/storage/tsconfig.json
COPY packages/pipeline/src packages/pipeline/src
COPY packages/pipeline/tsconfig.json packages/pipeline/tsconfig.json
# COPY packages/output/src packages/output/src # uncomment when package is initialized
# COPY packages/output/tsconfig.json packages/output/tsconfig.json

COPY apps/api/src apps/api/src
COPY apps/api/scripts apps/api/scripts
COPY apps/api/tsconfig.json apps/api/tsconfig.json
COPY apps/studio/src apps/studio/src
COPY apps/studio/tsconfig.json apps/studio/tsconfig.json
COPY apps/studio/index.html apps/studio/index.html
COPY apps/studio/vite.config.ts apps/studio/vite.config.ts
COPY apps/studio/components.json apps/studio/components.json

# Copy read-only code assets required during build (prompts, templates, global config)
# Read-only code assets required during build (prompts, templates, global config)
COPY prompts/ ./prompts/
COPY templates/ ./templates/
COPY config.yaml ./config.yaml
Expand All @@ -70,6 +50,28 @@ RUN pnpm build
# Bundle the API with esbuild (produces dist/api-server.mjs with correct ESM imports)
RUN pnpm --filter @adt/api build:server

# esbuild, tailwindcss, and postcss are dynamically imported by the packaging stage and
# cannot be bundled — they locate native binaries and CSS assets relative to their own
# package directory, which breaks when inlined into the bundle. Install all three with
# their full dependency tree into dist/node_modules/ using npm (not pnpm — npm ignores
# the monorepo workspace config and installs freely into a non-workspace directory).
# npm also installs @esbuild/linux-x64 automatically as esbuild's optional dependency.
# Versions are read directly from packages/pipeline/package.json to avoid drift.
RUN --mount=type=cache,target=/root/.npm \
node -e " \
const p = JSON.parse(require('fs').readFileSync('packages/pipeline/package.json', 'utf8')); \
require('fs').writeFileSync('apps/api/dist/package.json', JSON.stringify({ \
name: 'api-runtime', version: '0.0.0', \
dependencies: { \
esbuild: p.devDependencies.esbuild, \
tailwindcss: p.dependencies.tailwindcss, \
postcss: p.dependencies.postcss \
} \
})); \
" && \
npm install --prefix apps/api/dist --omit=dev --cache /root/.npm && \
rm -f apps/api/dist/package.json apps/api/dist/package-lock.json

# Build the studio SPA (Vite)
RUN pnpm --filter @adt/studio build

Expand All @@ -93,7 +95,9 @@ WORKDIR /app
# WASM files are copied into dist/ by the build:server script.
COPY --from=build /app/apps/api/dist ./apps/api/dist

# Copy baked-in defaults (overridable via volume mounts at runtime)
# Baked-in defaults (overridable via volume mounts at runtime).
# If a NEW top-level runtime directory is added to the repo (e.g. voices/, styleguides/),
# add a corresponding COPY --from=build line here and in the app stage below.
COPY --from=build /app/prompts/ ./prompts/
COPY --from=build /app/templates/ ./templates/
COPY --from=build /app/config.yaml ./config.yaml
Expand Down Expand Up @@ -162,7 +166,9 @@ COPY docker/nginx-single.conf /etc/nginx/http.d/default.conf
COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

# Baked-in defaults (overridable via volume mounts at runtime)
# Baked-in defaults (overridable via volume mounts at runtime).
# If a NEW top-level runtime directory is added to the repo (e.g. voices/, styleguides/),
# add a corresponding COPY --from=build line here and in the api stage above.
COPY --from=build /app/prompts/ ./prompts/
COPY --from=build /app/templates/ ./templates/
COPY --from=build /app/config.yaml ./config.yaml
Expand Down
6 changes: 6 additions & 0 deletions apps/api/scripts/bundle-server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ await build({
target: "node22",
format: "esm",
outfile: path.join(outDir, "api-server.mjs"),
// These packages cannot be bundled — they locate assets (native binaries, CSS files)
// via paths relative to their own package directory, which breaks when inlined.
// All three are installed into dist/node_modules/ by the `npm install --prefix dist/`
// step in the Dockerfile build stage. npm handles esbuild's platform binary
// (@esbuild/linux-x64) as an optional dependency automatically.
external: ["esbuild", "tailwindcss", "postcss"],
banner: {
js: [
// Polyfill __dirname, __filename, and require for ESM
Expand Down
13 changes: 13 additions & 0 deletions apps/api/src/routes/stages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,5 +263,18 @@ export function createStageRoutes(
return c.json({ ...active, queue })
})

// Removed Feb 2026: POST /books/:label/steps/run was renamed to /stages/run.
// Return 410 Gone with an actionable message so callers know exactly what changed.
app.post("/books/:label/steps/run", (c) => {
return c.json(
{
error:
"This endpoint was removed. Use POST /books/:label/stages/run " +
"with body { fromStage: string, toStage: string }.",
},
410
)
})
Comment on lines +268 to +277
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deprecated endpoint handler should include test coverage. Other route files in this codebase (books.test.ts, pages.test.ts, prompts.test.ts, etc.) have comprehensive test coverage, but stages.ts has no test file. Consider adding a test to verify that requests to the deprecated endpoint return a 410 status with the expected error message.

Copilot uses AI. Check for mistakes.

return app
}
2 changes: 1 addition & 1 deletion docker/nginx-single.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ server {
gzip_min_length 256;

# Proxy API requests to the Node.js server running in the same container
location /api/ {
location ^~ /api/ {
client_max_body_size 200M;

proxy_pass http://127.0.0.1:3001/api/;
Expand Down
2 changes: 1 addition & 1 deletion docker/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ server {
gzip_min_length 256;

# Proxy API requests to the backend
location /api/ {
location ^~ /api/ {
client_max_body_size 200M;

proxy_pass http://api:3001/api/;
Expand Down