From 2c809d55c09d1a4ac26d5a30f9a93eb26d0123b8 Mon Sep 17 00:00:00 2001 From: Victor Duarte Date: Thu, 5 Mar 2026 15:07:54 +0100 Subject: [PATCH 01/15] move docker into `authenticated` deployment mode --- Dockerfile | 2 +- docker-compose.quickstart.yml | 3 +++ docker-compose.yml | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 2339d2ff..dd4c8a84 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,7 +37,7 @@ ENV NODE_ENV=production \ PAPERCLIP_HOME=/paperclip \ PAPERCLIP_INSTANCE_ID=default \ PAPERCLIP_CONFIG=/paperclip/instances/default/config.json \ - PAPERCLIP_DEPLOYMENT_MODE=local_trusted \ + PAPERCLIP_DEPLOYMENT_MODE=authenticated \ PAPERCLIP_DEPLOYMENT_EXPOSURE=private VOLUME ["/paperclip"] diff --git a/docker-compose.quickstart.yml b/docker-compose.quickstart.yml index 373c5d48..f41c7388 100644 --- a/docker-compose.quickstart.yml +++ b/docker-compose.quickstart.yml @@ -10,5 +10,8 @@ services: PAPERCLIP_HOME: "/paperclip" OPENAI_API_KEY: "${OPENAI_API_KEY:-}" ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY:-}" + PAPERCLIP_DEPLOYMENT_MODE: "authenticated" + PAPERCLIP_DEPLOYMENT_EXPOSURE: "private" + BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:-dev-insecure-change-me}" volumes: - "${PAPERCLIP_DATA_DIR:-./data/docker-paperclip}:/paperclip" diff --git a/docker-compose.yml b/docker-compose.yml index d3cdc6ad..e0cea4f0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,9 @@ services: DATABASE_URL: postgres://paperclip:paperclip@db:5432/paperclip PORT: "3100" SERVE_UI: "true" + PAPERCLIP_DEPLOYMENT_MODE: "authenticated" + PAPERCLIP_DEPLOYMENT_EXPOSURE: "private" + BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:-dev-insecure-change-me}" depends_on: - db From e08362b66703371c3f1f84651d0db5deae65158f Mon Sep 17 00:00:00 2001 From: Victor Duarte Date: Thu, 5 Mar 2026 15:08:36 +0100 Subject: [PATCH 02/15] update docker base image --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index dd4c8a84..fcfd290d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-bookworm-slim AS base +FROM node:lts-trixie-slim AS base RUN apt-get update \ && apt-get install -y --no-install-recommends ca-certificates curl git \ && rm -rf /var/lib/apt/lists/* From d2dd8d0cc5aff45964c0728431c24bb4ea2ab3e2 Mon Sep 17 00:00:00 2001 From: zvictor Date: Thu, 5 Mar 2026 11:38:38 -0300 Subject: [PATCH 03/15] fix incorrect pkg scope --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index fcfd290d..453b3aa6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,8 +21,8 @@ FROM base AS build WORKDIR /app COPY --from=deps /app /app COPY . . -RUN pnpm --filter @paperclip/ui build -RUN pnpm --filter @paperclip/server build +RUN pnpm --filter @paperclipai/ui build +RUN pnpm --filter @paperclipai/server build FROM base AS production WORKDIR /app From 1f261d90f3ef49c7e2af6e854ba8e9047205c47d Mon Sep 17 00:00:00 2001 From: zvictor Date: Thu, 5 Mar 2026 11:39:13 -0300 Subject: [PATCH 04/15] add missing `openclaw` adapter from deps stage --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 453b3aa6..fea8ddb1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,6 +15,7 @@ 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/ +COPY packages/adapters/openclaw/package.json packages/adapters/openclaw/ RUN pnpm install --frozen-lockfile FROM base AS build From e5049a448ebe9735a3e388718cc9835f3ca4f507 Mon Sep 17 00:00:00 2001 From: zvictor Date: Thu, 5 Mar 2026 12:39:37 -0300 Subject: [PATCH 05/15] update typing to node v24 from v20 --- packages/adapter-utils/package.json | 1 + packages/adapter-utils/src/server-utils.ts | 18 ++++++--- packages/adapters/claude-local/package.json | 1 + packages/adapters/codex-local/package.json | 1 + packages/adapters/openclaw/package.json | 1 + packages/db/package.json | 1 + server/package.json | 1 + server/src/index.ts | 2 +- server/src/realtime/live-events-ws.ts | 42 ++++++++++++++++++--- 9 files changed, 56 insertions(+), 12 deletions(-) diff --git a/packages/adapter-utils/package.json b/packages/adapter-utils/package.json index 8a9411af..118eb895 100644 --- a/packages/adapter-utils/package.json +++ b/packages/adapter-utils/package.json @@ -30,6 +30,7 @@ "typecheck": "tsc --noEmit" }, "devDependencies": { + "@types/node": "^24.6.0", "typescript": "^5.7.3" } } diff --git a/packages/adapter-utils/src/server-utils.ts b/packages/adapter-utils/src/server-utils.ts index 1c8b76bd..76efba86 100644 --- a/packages/adapter-utils/src/server-utils.ts +++ b/packages/adapter-utils/src/server-utils.ts @@ -15,6 +15,14 @@ interface RunningProcess { graceSec: number; } +type ChildProcessWithEvents = ChildProcess & { + on(event: "error", listener: (err: Error) => void): ChildProcess; + on( + event: "close", + listener: (code: number | null, signal: NodeJS.Signals | null) => void, + ): ChildProcess; +}; + export const runningProcesses = new Map(); export const MAX_CAPTURE_BYTES = 4 * 1024 * 1024; export const MAX_EXCERPT_BYTES = 32 * 1024; @@ -217,7 +225,7 @@ export async function runChildProcess( env: mergedEnv, shell: false, stdio: [opts.stdin != null ? "pipe" : "ignore", "pipe", "pipe"], - }); + }) as ChildProcessWithEvents; if (opts.stdin != null && child.stdin) { child.stdin.write(opts.stdin); @@ -244,7 +252,7 @@ export async function runChildProcess( }, opts.timeoutSec * 1000) : null; - child.stdout?.on("data", (chunk) => { + child.stdout?.on("data", (chunk: unknown) => { const text = String(chunk); stdout = appendWithCap(stdout, text); logChain = logChain @@ -252,7 +260,7 @@ export async function runChildProcess( .catch((err) => onLogError(err, runId, "failed to append stdout log chunk")); }); - child.stderr?.on("data", (chunk) => { + child.stderr?.on("data", (chunk: unknown) => { const text = String(chunk); stderr = appendWithCap(stderr, text); logChain = logChain @@ -260,7 +268,7 @@ export async function runChildProcess( .catch((err) => onLogError(err, runId, "failed to append stderr log chunk")); }); - child.on("error", (err) => { + child.on("error", (err: Error) => { if (timeout) clearTimeout(timeout); runningProcesses.delete(runId); const errno = (err as NodeJS.ErrnoException).code; @@ -272,7 +280,7 @@ export async function runChildProcess( reject(new Error(msg)); }); - child.on("close", (code, signal) => { + child.on("close", (code: number | null, signal: NodeJS.Signals | null) => { if (timeout) clearTimeout(timeout); runningProcesses.delete(runId); void logChain.finally(() => { diff --git a/packages/adapters/claude-local/package.json b/packages/adapters/claude-local/package.json index faa16b64..c999013d 100644 --- a/packages/adapters/claude-local/package.json +++ b/packages/adapters/claude-local/package.json @@ -45,6 +45,7 @@ "picocolors": "^1.1.1" }, "devDependencies": { + "@types/node": "^24.6.0", "typescript": "^5.7.3" } } diff --git a/packages/adapters/codex-local/package.json b/packages/adapters/codex-local/package.json index 9fc9b581..e6853aa7 100644 --- a/packages/adapters/codex-local/package.json +++ b/packages/adapters/codex-local/package.json @@ -45,6 +45,7 @@ "picocolors": "^1.1.1" }, "devDependencies": { + "@types/node": "^24.6.0", "typescript": "^5.7.3" } } diff --git a/packages/adapters/openclaw/package.json b/packages/adapters/openclaw/package.json index 22acb5e3..c8bd561d 100644 --- a/packages/adapters/openclaw/package.json +++ b/packages/adapters/openclaw/package.json @@ -44,6 +44,7 @@ "picocolors": "^1.1.1" }, "devDependencies": { + "@types/node": "^24.6.0", "typescript": "^5.7.3" } } diff --git a/packages/db/package.json b/packages/db/package.json index 845d5487..0a0b4521 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -38,6 +38,7 @@ "postgres": "^3.4.5" }, "devDependencies": { + "@types/node": "^24.6.0", "drizzle-kit": "^0.31.9", "tsx": "^4.19.2", "typescript": "^5.7.3", diff --git a/server/package.json b/server/package.json index 781f452a..e479eafe 100644 --- a/server/package.json +++ b/server/package.json @@ -58,6 +58,7 @@ "@types/express-serve-static-core": "^5.0.0", "@types/multer": "^2.0.0", "@types/supertest": "^6.0.2", + "@types/ws": "^8.18.1", "supertest": "^7.0.0", "tsx": "^4.19.2", "typescript": "^5.7.3", diff --git a/server/src/index.ts b/server/src/index.ts index ada5743f..125d3021 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -444,7 +444,7 @@ const app = await createApp(db as any, { betterAuthHandler, resolveSession, }); -const server = createServer(app); +const server = createServer(app as unknown as Parameters[0]); const listenPort = await detectPort(config.port); if (listenPort !== config.port) { diff --git a/server/src/realtime/live-events-ws.ts b/server/src/realtime/live-events-ws.ts index b082ecb6..d18e2930 100644 --- a/server/src/realtime/live-events-ws.ts +++ b/server/src/realtime/live-events-ws.ts @@ -1,15 +1,45 @@ import { createHash } from "node:crypto"; import type { IncomingMessage, Server as HttpServer } from "node:http"; +import { createRequire } from "node:module"; import type { Duplex } from "node:stream"; import { and, eq, isNull } from "drizzle-orm"; import type { Db } from "@paperclipai/db"; import { agentApiKeys, companyMemberships, instanceUserRoles } from "@paperclipai/db"; import type { DeploymentMode } from "@paperclipai/shared"; -import { WebSocket, WebSocketServer } from "ws"; import type { BetterAuthSessionResult } from "../auth/better-auth.js"; import { logger } from "../middleware/logger.js"; import { subscribeCompanyLiveEvents } from "../services/live-events.js"; +interface WsSocket { + readyState: number; + ping(): void; + send(data: string): void; + terminate(): void; + close(code?: number, reason?: string): void; + on(event: "pong", listener: () => void): void; + on(event: "close", listener: () => void): void; + on(event: "error", listener: (err: Error) => void): void; +} + +interface WsServer { + clients: Set; + on(event: "connection", listener: (socket: WsSocket, req: IncomingMessage) => void): void; + on(event: "close", listener: () => void): void; + handleUpgrade( + req: IncomingMessage, + socket: Duplex, + head: Buffer, + callback: (ws: WsSocket) => void, + ): void; + emit(event: "connection", ws: WsSocket, req: IncomingMessage): boolean; +} + +const require = createRequire(import.meta.url); +const { WebSocket, WebSocketServer } = require("ws") as { + WebSocket: { OPEN: number }; + WebSocketServer: new (opts: { noServer: boolean }) => WsServer; +}; + interface UpgradeContext { companyId: string; actorType: "board" | "agent"; @@ -154,8 +184,8 @@ export function setupLiveEventsWebSocketServer( }, ) { const wss = new WebSocketServer({ noServer: true }); - const cleanupByClient = new Map void>(); - const aliveByClient = new Map(); + const cleanupByClient = new Map void>(); + const aliveByClient = new Map(); const pingInterval = setInterval(() => { for (const socket of wss.clients) { @@ -168,7 +198,7 @@ export function setupLiveEventsWebSocketServer( } }, 30000); - wss.on("connection", (socket, req) => { + wss.on("connection", (socket: WsSocket, req: IncomingMessage) => { const context = (req as IncomingMessageWithContext).paperclipUpgradeContext; if (!context) { socket.close(1008, "missing context"); @@ -194,7 +224,7 @@ export function setupLiveEventsWebSocketServer( aliveByClient.delete(socket); }); - socket.on("error", (err) => { + socket.on("error", (err: Error) => { logger.warn({ err, companyId: context.companyId }, "live websocket client error"); }); }); @@ -229,7 +259,7 @@ export function setupLiveEventsWebSocketServer( const reqWithContext = req as IncomingMessageWithContext; reqWithContext.paperclipUpgradeContext = context; - wss.handleUpgrade(req, socket, head, (ws) => { + wss.handleUpgrade(req, socket, head, (ws: WsSocket) => { wss.emit("connection", ws, reqWithContext); }); }) From c610951a71ce7566969d52a49850c60399a69036 Mon Sep 17 00:00:00 2001 From: zvictor Date: Thu, 5 Mar 2026 12:39:50 -0300 Subject: [PATCH 06/15] update lock file --- pnpm-lock.yaml | 135 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 85f9d4b2..c96ed162 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -78,6 +78,9 @@ importers: packages/adapter-utils: devDependencies: + '@types/node': + specifier: ^24.6.0 + version: 24.11.0 typescript: specifier: ^5.7.3 version: 5.9.3 @@ -91,6 +94,9 @@ importers: specifier: ^1.1.1 version: 1.1.1 devDependencies: + '@types/node': + specifier: ^24.6.0 + version: 24.11.0 typescript: specifier: ^5.7.3 version: 5.9.3 @@ -104,6 +110,9 @@ importers: specifier: ^1.1.1 version: 1.1.1 devDependencies: + '@types/node': + specifier: ^24.6.0 + version: 24.11.0 typescript: specifier: ^5.7.3 version: 5.9.3 @@ -130,6 +139,9 @@ importers: specifier: ^1.1.1 version: 1.1.1 devDependencies: + '@types/node': + specifier: ^24.6.0 + version: 24.11.0 typescript: specifier: ^5.7.3 version: 5.9.3 @@ -159,6 +171,9 @@ importers: specifier: ^3.4.5 version: 3.4.8 devDependencies: + '@types/node': + specifier: ^24.6.0 + version: 24.11.0 drizzle-kit: specifier: ^0.31.9 version: 0.31.9 @@ -170,7 +185,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) packages/shared: dependencies: @@ -263,6 +278,9 @@ importers: '@types/supertest': specifier: ^6.0.2 version: 6.0.3 + '@types/ws': + specifier: ^8.18.1 + version: 8.18.1 supertest: specifier: ^7.0.0 version: 7.2.2 @@ -2816,6 +2834,9 @@ packages: '@types/node@22.19.11': resolution: {integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==} + '@types/node@24.11.0': + resolution: {integrity: sha512-fPxQqz4VTgPI/IQ+lj9r0h+fDR66bzoeMGHp8ASee+32OSGIkeASsoZuJixsQoVef1QJbeubcPBxKk22QVoWdw==} + '@types/node@25.2.3': resolution: {integrity: sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==} @@ -2851,6 +2872,9 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} @@ -8194,6 +8218,10 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/node@24.11.0': + dependencies: + undici-types: 7.16.0 + '@types/node@25.2.3': dependencies: undici-types: 7.16.0 @@ -8235,6 +8263,10 @@ snapshots: '@types/unist@3.0.3': {} + '@types/ws@8.18.1': + dependencies: + '@types/node': 25.2.3 + '@ungap/structured-clone@1.3.0': {} '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': @@ -8257,6 +8289,14 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 + '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.1(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': dependencies: '@vitest/spy': 3.2.4 @@ -10601,6 +10641,27 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 + vite-node@3.2.4(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 6.4.1(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vite-node@3.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): dependencies: cac: 6.7.14 @@ -10622,6 +10683,21 @@ snapshots: - tsx - yaml + vite@6.4.1(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.57.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.11.0 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + tsx: 4.21.0 + vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): dependencies: esbuild: 0.25.12 @@ -10637,6 +10713,21 @@ snapshots: lightningcss: 1.30.2 tsx: 4.21.0 + vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.57.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.11.0 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + tsx: 4.21.0 + vite@7.3.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): dependencies: esbuild: 0.27.3 @@ -10652,6 +10743,48 @@ snapshots: lightningcss: 1.30.2 tsx: 4.21.0 + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.3.1(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + vite-node: 3.2.4(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 24.11.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): dependencies: '@types/chai': 5.2.3 From 57db28e9e6765f5cb4a7556763630b5bbf5da8c5 Mon Sep 17 00:00:00 2001 From: zvictor Date: Thu, 5 Mar 2026 12:55:10 -0300 Subject: [PATCH 07/15] wait for a health db --- docker-compose.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index e0cea4f0..3de17a57 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,11 @@ services: POSTGRES_USER: paperclip POSTGRES_PASSWORD: paperclip POSTGRES_DB: paperclip + healthcheck: + test: ["CMD-SHELL", "pg_isready -U paperclip -d paperclip"] + interval: 2s + timeout: 5s + retries: 30 ports: - "5432:5432" volumes: @@ -22,7 +27,8 @@ services: PAPERCLIP_DEPLOYMENT_EXPOSURE: "private" BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:-dev-insecure-change-me}" depends_on: - - db + db: + condition: service_healthy volumes: pgdata: From 1e5e09f0fa00d2021c9e5f8a503e916debaacaeb Mon Sep 17 00:00:00 2001 From: zvictor Date: Thu, 5 Mar 2026 14:19:28 -0300 Subject: [PATCH 08/15] expose `PAPERCLIP_ALLOWED_HOSTNAMES` in compose files --- docker-compose.quickstart.yml | 1 + docker-compose.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/docker-compose.quickstart.yml b/docker-compose.quickstart.yml index f41c7388..f44549b4 100644 --- a/docker-compose.quickstart.yml +++ b/docker-compose.quickstart.yml @@ -12,6 +12,7 @@ services: ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY:-}" PAPERCLIP_DEPLOYMENT_MODE: "authenticated" PAPERCLIP_DEPLOYMENT_EXPOSURE: "private" + PAPERCLIP_ALLOWED_HOSTNAMES: "${PAPERCLIP_ALLOWED_HOSTNAMES:-localhost}" BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:-dev-insecure-change-me}" volumes: - "${PAPERCLIP_DATA_DIR:-./data/docker-paperclip}:/paperclip" diff --git a/docker-compose.yml b/docker-compose.yml index 3de17a57..969d620b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,6 +25,7 @@ services: SERVE_UI: "true" PAPERCLIP_DEPLOYMENT_MODE: "authenticated" PAPERCLIP_DEPLOYMENT_EXPOSURE: "private" + PAPERCLIP_ALLOWED_HOSTNAMES: "${PAPERCLIP_ALLOWED_HOSTNAMES:-localhost}" BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:-dev-insecure-change-me}" depends_on: db: From 4b8e880a961d073bfef540b7c1f368397edd270e Mon Sep 17 00:00:00 2001 From: zvictor Date: Thu, 5 Mar 2026 14:20:35 -0300 Subject: [PATCH 09/15] remove an insecure default auth secret --- docker-compose.quickstart.yml | 2 +- docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.quickstart.yml b/docker-compose.quickstart.yml index f44549b4..4193c2f9 100644 --- a/docker-compose.quickstart.yml +++ b/docker-compose.quickstart.yml @@ -13,6 +13,6 @@ services: PAPERCLIP_DEPLOYMENT_MODE: "authenticated" PAPERCLIP_DEPLOYMENT_EXPOSURE: "private" PAPERCLIP_ALLOWED_HOSTNAMES: "${PAPERCLIP_ALLOWED_HOSTNAMES:-localhost}" - BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:-dev-insecure-change-me}" + BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:?BETTER_AUTH_SECRET must be set}" volumes: - "${PAPERCLIP_DATA_DIR:-./data/docker-paperclip}:/paperclip" diff --git a/docker-compose.yml b/docker-compose.yml index 969d620b..64817b55 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,7 @@ services: PAPERCLIP_DEPLOYMENT_MODE: "authenticated" PAPERCLIP_DEPLOYMENT_EXPOSURE: "private" PAPERCLIP_ALLOWED_HOSTNAMES: "${PAPERCLIP_ALLOWED_HOSTNAMES:-localhost}" - BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:-dev-insecure-change-me}" + BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:?BETTER_AUTH_SECRET must be set}" depends_on: db: condition: service_healthy From 0d36cf00f892793aba4cca70601d843855ba90c1 Mon Sep 17 00:00:00 2001 From: zvictor Date: Thu, 5 Mar 2026 14:22:45 -0300 Subject: [PATCH 10/15] Add artifact-check to fail fast on broken builds --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index fea8ddb1..1bfccd1d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,6 +24,7 @@ COPY --from=deps /app /app COPY . . RUN pnpm --filter @paperclipai/ui build RUN pnpm --filter @paperclipai/server build +RUN test -f server/dist/index.js || (echo "ERROR: server build output missing" && exit 1) FROM base AS production WORKDIR /app From f75a4d95896dcd365de6e750778d2a074e262b59 Mon Sep 17 00:00:00 2001 From: zvictor Date: Thu, 5 Mar 2026 14:24:00 -0300 Subject: [PATCH 11/15] force `@types/node@24` in the server --- pnpm-lock.yaml | 13 ++++++++----- server/package.json | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c96ed162..fcfb7e78 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -228,7 +228,7 @@ importers: version: link:../packages/shared better-auth: specifier: 1.4.18 - version: 1.4.18(drizzle-kit@0.31.9)(drizzle-orm@0.38.4(@electric-sql/pglite@0.3.15)(@types/react@19.2.14)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8)(react@19.2.4))(pg@8.18.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + version: 1.4.18(drizzle-kit@0.31.9)(drizzle-orm@0.38.4(@electric-sql/pglite@0.3.15)(@types/react@19.2.14)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8)(react@19.2.4))(pg@8.18.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) detect-port: specifier: ^2.1.0 version: 2.1.0 @@ -275,6 +275,9 @@ importers: '@types/multer': specifier: ^2.0.0 version: 2.0.0 + '@types/node': + specifier: ^24.6.0 + version: 24.11.0 '@types/supertest': specifier: ^6.0.2 version: 6.0.3 @@ -292,10 +295,10 @@ importers: version: 5.9.3 vite: specifier: ^6.1.0 - version: 6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 6.4.1(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) ui: dependencies: @@ -8380,7 +8383,7 @@ snapshots: baseline-browser-mapping@2.9.19: {} - better-auth@1.4.18(drizzle-kit@0.31.9)(drizzle-orm@0.38.4(@electric-sql/pglite@0.3.15)(@types/react@19.2.14)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8)(react@19.2.4))(pg@8.18.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)): + better-auth@1.4.18(drizzle-kit@0.31.9)(drizzle-orm@0.38.4(@electric-sql/pglite@0.3.15)(@types/react@19.2.14)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8)(react@19.2.4))(pg@8.18.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)): dependencies: '@better-auth/core': 1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0) '@better-auth/telemetry': 1.4.18(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)) @@ -8400,7 +8403,7 @@ snapshots: pg: 8.18.0 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) better-call@1.1.8(zod@4.3.6): dependencies: diff --git a/server/package.json b/server/package.json index e479eafe..2e470111 100644 --- a/server/package.json +++ b/server/package.json @@ -57,6 +57,7 @@ "@types/express": "^5.0.0", "@types/express-serve-static-core": "^5.0.0", "@types/multer": "^2.0.0", + "@types/node": "^24.6.0", "@types/supertest": "^6.0.2", "@types/ws": "^8.18.1", "supertest": "^7.0.0", From 201d91b4f56f0bc9bf2f8c179dfa89dd18a00693 Mon Sep 17 00:00:00 2001 From: zvictor Date: Thu, 5 Mar 2026 14:53:42 -0300 Subject: [PATCH 12/15] add support to `cursor` and `opencode` in containerized instances --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 1bfccd1d..0fcc3216 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,9 @@ 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/ +COPY packages/adapters/cursor-local/package.json packages/adapters/cursor-local/ COPY packages/adapters/openclaw/package.json packages/adapters/openclaw/ +COPY packages/adapters/opencode-local/package.json packages/adapters/opencode-local/ RUN pnpm install --frozen-lockfile FROM base AS build From 4eedf15870f3401d905e40dfbc040b1069be6c57 Mon Sep 17 00:00:00 2001 From: zvictor Date: Thu, 5 Mar 2026 18:11:50 -0300 Subject: [PATCH 13/15] persist paperclip data in a named volume --- docker-compose.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 64817b55..039bb6b1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,9 +27,12 @@ services: PAPERCLIP_DEPLOYMENT_EXPOSURE: "private" PAPERCLIP_ALLOWED_HOSTNAMES: "${PAPERCLIP_ALLOWED_HOSTNAMES:-localhost}" BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:?BETTER_AUTH_SECRET must be set}" + volumes: + - paperclip-data:/paperclip depends_on: db: condition: service_healthy volumes: pgdata: + paperclip-data: From 7086ad00ae0b08cadf8ceee360ccb7d81cd99efb Mon Sep 17 00:00:00 2001 From: Arthur R Longbottom Date: Thu, 5 Mar 2026 16:13:29 -0800 Subject: [PATCH 14/15] feat(codex): add gpt-5.4 to codex_local model list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the newly released gpt-5.4 model to the codex_local adapter's available models list. にゃ~ 🐱 --- packages/adapters/codex-local/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/adapters/codex-local/src/index.ts b/packages/adapters/codex-local/src/index.ts index 3a499660..f09e50d9 100644 --- a/packages/adapters/codex-local/src/index.ts +++ b/packages/adapters/codex-local/src/index.ts @@ -4,6 +4,7 @@ export const DEFAULT_CODEX_LOCAL_MODEL = "gpt-5.3-codex"; export const DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX = true; export const models = [ + { id: "gpt-5.4", label: "gpt-5.4" }, { id: DEFAULT_CODEX_LOCAL_MODEL, label: DEFAULT_CODEX_LOCAL_MODEL }, { id: "gpt-5.3-codex-spark", label: "gpt-5.3-codex-spark" }, { id: "gpt-5", label: "gpt-5" }, From 88682632f90229c43908aa9a09c10813ac476a05 Mon Sep 17 00:00:00 2001 From: MumuTW Date: Fri, 6 Mar 2026 07:47:11 +0000 Subject: [PATCH 15/15] fix(ui): wrap failed run card actions on mobile --- ui/src/pages/Inbox.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/src/pages/Inbox.tsx b/ui/src/pages/Inbox.tsx index b11afecf..dbca65de 100644 --- a/ui/src/pages/Inbox.tsx +++ b/ui/src/pages/Inbox.tsx @@ -182,9 +182,9 @@ function FailedRunCard({ )} -
+
-
+
@@ -199,12 +199,12 @@ function FailedRunCard({ {sourceLabel} run failed {timeAgo(run.createdAt)}

-
+