diff --git a/ai/compose.yml b/ai/compose.yml index 004a5c9..003b3d4 100644 --- a/ai/compose.yml +++ b/ai/compose.yml @@ -115,7 +115,8 @@ services: - ai_backend paperclip: - image: ghcr.io/paperclipai/paperclip:v2026.517.0 + build: + context: ./paperclip container_name: paperclip restart: always ports: diff --git a/ai/paperclip/Dockerfile b/ai/paperclip/Dockerfile new file mode 100644 index 0000000..a40c91a --- /dev/null +++ b/ai/paperclip/Dockerfile @@ -0,0 +1,47 @@ +# syntax=docker/dockerfile:1.20 +FROM ghcr.io/paperclipai/paperclip:v2026.517.0 + +# ── Install Hermes adapter npm package into seed directory ────────── +# This seed data gets copied to the persistent volume on first boot +# so the adapter is available without network access. +USER root + +RUN npm install --no-save --prefix /opt/paperclip-seed/adapter-plugins \ + hermes-paperclip-adapter@0.3.0 + +# Create adapter-plugins.json metadata (Paperclip reads this on startup +# to discover which external adapters to load) +RUN mkdir -p /opt/paperclip-seed && python3 -c " +import json +record = { + 'packageName': 'hermes-paperclip-adapter', + 'version': '0.3.0', + 'type': 'hermes', + 'installedAt': '2026-05-18T00:00:00.000Z', +} +with open('/opt/paperclip-seed/adapter-plugins.json', 'w') as f: + json.dump([record], f, indent=2) +" + +# Ensure the adapter-plugins dir has a package.json (Paperclip expects one) +RUN python3 -c " +import json +pkg = { + 'name': 'paperclip-adapter-plugins', + 'version': '0.0.0', + 'private': True, + 'description': 'Managed directory for Paperclip external adapter plugins.', +} +with open('/opt/paperclip-seed/adapter-plugins/package.json', 'w') as f: + json.dump(pkg, f, indent=2) +" + +# ── Custom entrypoint ────────────────────────────────────────────── +# Seeds the Hermes adapter on fresh volumes, then runs original logic. +COPY docker-entrypoint.sh /usr/local/bin/paperclip-entrypoint.sh +RUN chmod +x /usr/local/bin/paperclip-entrypoint.sh + +USER node + +ENTRYPOINT ["/usr/local/bin/paperclip-entrypoint.sh"] +CMD ["node", "--import", "./server/node_modules/tsx/dist/loader.mjs", "server/dist/index.js"] diff --git a/ai/paperclip/docker-entrypoint.sh b/ai/paperclip/docker-entrypoint.sh new file mode 100644 index 0000000..28f561a --- /dev/null +++ b/ai/paperclip/docker-entrypoint.sh @@ -0,0 +1,35 @@ +#!/bin/sh +set -e + +# ── Seed Hermes adapter if volume is fresh ────────────────────────── +PAPERCLIP_HOME="${PAPERCLIP_HOME:-/paperclip}" +if [ ! -f "${PAPERCLIP_HOME}/adapter-plugins.json" ]; then + echo "[paperclip] Seeding Hermes adapter plugin..." + cp -r /opt/paperclip-seed/* "${PAPERCLIP_HOME}/" + chown -R "${USER_UID:-1000}:${USER_GID:-1000}" \ + "${PAPERCLIP_HOME}/adapter-plugins" \ + "${PAPERCLIP_HOME}/adapter-plugins.json" + echo "[paperclip] Hermes adapter seeded. Ready to create Hermes agents." +fi + +# ── Original entrypoint logic (UID/GID adjustment) ────────────────── +PUID="${USER_UID:-1000}" +PGID="${USER_GID:-1000}" +changed=0 + +if [ "$(id -u node)" -ne "$PUID" ]; then + usermod -o -u "$PUID" node + changed=1 +fi + +if [ "$(id -g node)" -ne "$PGID" ]; then + groupmod -o -g "$PGID" node + usermod -g "$PGID" node + changed=1 +fi + +if [ "$changed" = "1" ]; then + chown -R node:node "${PAPERCLIP_HOME}" +fi + +exec gosu node "$@"