From 4ae58e59a88e4f614570b8d65ebdc543c1843adb Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Fri, 30 Oct 2026 20:18:17 -0700 Subject: [PATCH 1/4] build(backend): drop dev dependencies from the runtime image The runtime stage ran `npm install --include=dev`, so jest, eslint, ts-node, typescript and mongodb-memory-server shipped to prod. Compile in a build stage; the runtime stage installs with `npm ci --omit=dev` and copies dist/ from the build stage. json5 is required at runtime by three modules and only resolved through dev dependencies (@babel/core, ts-jest). Declare it in dependencies at the locked 2.2.3. Written by Rivet, a Commonly agent Co-Authored-By: Claude Opus 5.5 --- backend/Dockerfile | 31 ++++++++++++++++++++++++------- backend/package-lock.json | 6 +++--- backend/package.json | 1 + 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index c4feea1d00..9e8e139bf9 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,3 +1,22 @@ +# Build stage: dev dependencies are needed for tsc and never ship. +FROM node:22 AS build + +WORKDIR /app + +COPY package*.json ./ + +# Install all dependencies including dev dependencies +RUN npm install --include=dev + +COPY . . + +# Compile TypeScript to dist/ +RUN npm run build + +# Copy non-JS assets that tsc doesn't copy +RUN cp -r external dist/external 2>/dev/null || true + +# Runtime stage FROM node:22 WORKDIR /app @@ -54,16 +73,14 @@ ENV OPENUI_TELEMETRY_DISABLED=1 COPY package*.json ./ -# Install all dependencies including dev dependencies -RUN npm install --include=dev +# Production dependencies only. Dev tooling (jest, eslint, ts-node, typescript, +# mongodb-memory-server, ...) stays in the build stage and never reaches prod. +RUN npm ci --omit=dev COPY . . -# Compile TypeScript to dist/ -RUN npm run build - -# Copy non-JS assets that tsc doesn't copy -RUN cp -r external dist/external 2>/dev/null || true +# Compiled output from the build stage: dist/ with config/ and external/ in place +COPY --from=build /app/dist ./dist EXPOSE 5000 diff --git a/backend/package-lock.json b/backend/package-lock.json index d95894451a..4860497aea 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -29,6 +29,7 @@ "express-async-errors": "3.1.1", "express-rate-limit": "^8.3.2", "helmet": "^7.1.0", + "json5": "^2.2.3", "jsonwebtoken": "^9.0.0", "mongodb": "^6.3.0", "mongoose": "^7.0.3", @@ -8978,7 +8979,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -19641,8 +19642,7 @@ "json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "jsonify": { "version": "0.0.1", diff --git a/backend/package.json b/backend/package.json index c8f05804ae..152ae378c7 100644 --- a/backend/package.json +++ b/backend/package.json @@ -61,6 +61,7 @@ "express-async-errors": "3.1.1", "express-rate-limit": "^8.3.2", "helmet": "^7.1.0", + "json5": "^2.2.3", "jsonwebtoken": "^9.0.0", "mongodb": "^6.3.0", "mongoose": "^7.0.3", From 3587711be2b92adb1300fa51e6d22567bdc295cd Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Fri, 30 Oct 2026 20:57:45 -0700 Subject: [PATCH 2/4] build(backend): run operator scripts from dist in the runtime image ts-node is a devDependency and is no longer in the runtime image, so the 14 operator scripts that ran through it (backfill:*, sweep:*, migrate:*, audit:*, revive:*, set:agent-descriptions) would fail inside the container. install.sh and CHANGELOG.md tell operators to run them with `docker compose exec backend npm run ...`. Each script now runs its compiled output: `node dist/scripts/.js`. `npm run build` already emits those files. The `dev` script keeps ts-node, which is local-only. Written by Rivet, a Commonly agent Co-Authored-By: Claude Opus 5.5 --- backend/package.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/backend/package.json b/backend/package.json index 152ae378c7..127fe4f6f8 100644 --- a/backend/package.json +++ b/backend/package.json @@ -8,7 +8,7 @@ "start": "node dist/server.js", "dev": "nodemon --exec 'ts-node --transpile-only' server.ts", "migrate-files": "node migrations/migrateFilesToDB.js", - "migrate:github-issue-write-capability": "ts-node scripts/migrate-github-issue-write-capability.ts", + "migrate:github-issue-write-capability": "node dist/scripts/migrate-github-issue-write-capability.js", "test": "jest", "test:temp-cleanup": "node scripts/verify-jest-temp-cleanup.js", "test:watch": "jest --watch", @@ -23,19 +23,19 @@ "discord:register": "node scripts/register-discord-commands.js", "discord:list": "node scripts/register-discord-commands.js list", "bootstrap:clawd-bot": "node scripts/bootstrap-clawd-bot.js", - "backfill:attention-items": "ts-node scripts/backfill-attention-items.ts", - "backfill:message-author-kind": "ts-node scripts/backfill-message-author-kind.ts", - "backfill:local-messages": "ts-node scripts/backfill-local-messages-to-pg.ts", - "migrate:task-priority": "ts-node scripts/migrate-task-priority.ts", - "migrate:task-source-ref-identity": "ts-node scripts/migrate-task-source-ref-identity.ts", - "sweep:resolved-mention-attention": "ts-node scripts/sweep-resolved-mention-attention.ts", - "audit:replied-attention-stamps": "ts-node scripts/audit-replied-attention-stamps.ts", - "sweep:done-task-handoffs": "ts-node scripts/sweep-done-task-handoffs.ts", - "sweep:leftover-runtime-tokens": "ts-node scripts/sweep-leftover-runtime-tokens.ts", - "sweep:stale-digest": "ts-node scripts/sweep-stale-digest.ts", - "backfill:hosted-mcp-client-id": "ts-node scripts/backfill-hosted-mcp-client-id.ts", - "revive:native-installations": "ts-node scripts/revive-native-installations.ts", - "set:agent-descriptions": "ts-node scripts/set-agent-descriptions.ts", + "backfill:attention-items": "node dist/scripts/backfill-attention-items.js", + "backfill:message-author-kind": "node dist/scripts/backfill-message-author-kind.js", + "backfill:local-messages": "node dist/scripts/backfill-local-messages-to-pg.js", + "migrate:task-priority": "node dist/scripts/migrate-task-priority.js", + "migrate:task-source-ref-identity": "node dist/scripts/migrate-task-source-ref-identity.js", + "sweep:resolved-mention-attention": "node dist/scripts/sweep-resolved-mention-attention.js", + "audit:replied-attention-stamps": "node dist/scripts/audit-replied-attention-stamps.js", + "sweep:done-task-handoffs": "node dist/scripts/sweep-done-task-handoffs.js", + "sweep:leftover-runtime-tokens": "node dist/scripts/sweep-leftover-runtime-tokens.js", + "sweep:stale-digest": "node dist/scripts/sweep-stale-digest.js", + "backfill:hosted-mcp-client-id": "node dist/scripts/backfill-hosted-mcp-client-id.js", + "revive:native-installations": "node dist/scripts/revive-native-installations.js", + "set:agent-descriptions": "node dist/scripts/set-agent-descriptions.js", "sync:seats-record": "node scripts/sync-seats-record.js", "tsc:check": "tsc --noEmit -p tsconfig.typescheck.json", "tsc:check:all": "tsc --noEmit" From a8c1d1ccc657649cdd1e5c75fd3471b44f7b6916 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Fri, 30 Oct 2026 21:09:02 -0700 Subject: [PATCH 3/4] test(backend): pin the migration script to its compiled target The runtime image no longer ships ts-node, so the operator script is run from dist/. Update the package-script assertion to match. Written by Rivet, a Commonly agent. Co-Authored-By: Claude Opus 5.5 Co-authored-by: rivet Agent-Model: claude/claude-haiku-5-5/default --- .../unit/scripts/migrate-github-issue-write-capability.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/__tests__/unit/scripts/migrate-github-issue-write-capability.test.js b/backend/__tests__/unit/scripts/migrate-github-issue-write-capability.test.js index dbf1b03489..c5997ced9d 100644 --- a/backend/__tests__/unit/scripts/migrate-github-issue-write-capability.test.js +++ b/backend/__tests__/unit/scripts/migrate-github-issue-write-capability.test.js @@ -29,7 +29,7 @@ describe('migrateGitHubIssueWriteCapability', () => { test('exposes the migration through the backend package scripts for deploy operators', () => { expect(backendPackage.scripts['migrate:github-issue-write-capability']) - .toContain('migrate-github-issue-write-capability.ts'); + .toBe('node dist/scripts/migrate-github-issue-write-capability.js'); }); test('grants every legacy install for a configured dev identity once, while leaving other identities ungranted', async () => { From f8b4bba29f42f6bf68bec0ff6a0c01e090548e45 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Fri, 30 Oct 2026 21:50:15 -0700 Subject: [PATCH 4/4] docs(runbooks): run token-copy scrubs from compiled dist, not ts-node ops-runner runs the built image, which has no ts-node (dev-only). Point both runbooks at node dist/scripts/.js. Written by Rivet, a Commonly agent. Co-Authored-By: Claude Haiku 5.5 Co-authored-by: rivet Agent-Model: claude/claude-haiku-5-5/default --- docs/runbooks/migrate-chat-token-copies.md | 6 +++--- docs/runbooks/scrub-feed-token-copies.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/runbooks/migrate-chat-token-copies.md b/docs/runbooks/migrate-chat-token-copies.md index 88d474530f..3f4ad540cc 100644 --- a/docs/runbooks/migrate-chat-token-copies.md +++ b/docs/runbooks/migrate-chat-token-copies.md @@ -36,10 +36,10 @@ key ring (`CONNECTOR_SECRET_KEYS`, `CONNECTOR_SECRET_ACTIVE_KEY`) supplied by th existing ops-runner secret environment (never paste them into a command): ```sh -npx ts-node scripts/migrate-chat-token-copies.ts +node dist/scripts/migrate-chat-token-copies.js # Review the DRY-RUN counts by type, then explicitly apply: -npx ts-node scripts/migrate-chat-token-copies.ts --apply -npx ts-node scripts/migrate-chat-token-copies.ts +node dist/scripts/migrate-chat-token-copies.js --apply +node dist/scripts/migrate-chat-token-copies.js ``` **What the counts mean.** The final dry run should report zero candidates and diff --git a/docs/runbooks/scrub-feed-token-copies.md b/docs/runbooks/scrub-feed-token-copies.md index e06381710b..917e5c3525 100644 --- a/docs/runbooks/scrub-feed-token-copies.md +++ b/docs/runbooks/scrub-feed-token-copies.md @@ -17,10 +17,10 @@ From the deployed backend directory, with `MONGO_URI` supplied by the existing ops-runner secret environment (never paste it into a command): ```sh -npx ts-node scripts/scrub-feed-token-copies.ts +node dist/scripts/scrub-feed-token-copies.js # Review DRY-RUN counts by type, then explicitly apply: -npx ts-node scripts/scrub-feed-token-copies.ts --apply -npx ts-node scripts/scrub-feed-token-copies.ts +node dist/scripts/scrub-feed-token-copies.js --apply +node dist/scripts/scrub-feed-token-copies.js ``` The final dry run should report zero candidates for both types. Reports contain