--- - name: Deploy Paperclip on openclaw hosts: openclaw become: true gather_facts: true vars: paperclip_user: dan paperclip_home: /home/dan paperclip_config_path: /home/dan/.paperclip/instances/default/config.json paperclip_logs_dir: /home/dan/.paperclip/instances/default/logs paperclip_service_name: paperclip paperclip_host: "0.0.0.0" paperclip_port: 3100 paperclip_allowed_hostnames: - "192.168.2.88" - "openclaw" tasks: - name: Ensure Paperclip logs directory exists ansible.builtin.file: path: "{{ paperclip_logs_dir }}" state: directory owner: "{{ paperclip_user }}" group: "{{ paperclip_user }}" mode: "0755" - name: Ensure Paperclip config has LAN authenticated private mode ansible.builtin.shell: | python3 - <<'PY' import json from pathlib import Path config_path = Path("{{ paperclip_config_path }}") if not config_path.exists(): raise SystemExit(f"Missing config file: {config_path}") data = json.loads(config_path.read_text()) server = data.setdefault("server", {}) before = json.dumps(server, sort_keys=True) server["deploymentMode"] = "authenticated" server["exposure"] = "private" server["host"] = "{{ paperclip_host }}" server["port"] = {{ paperclip_port }} allowed = set(server.get("allowedHostnames", [])) allowed.update({{ paperclip_allowed_hostnames | to_json }}) server["allowedHostnames"] = sorted(allowed) server["serveUi"] = True after = json.dumps(server, sort_keys=True) if before != after: config_path.write_text(json.dumps(data, indent=2) + "\n") print("changed") else: print("unchanged") PY args: executable: /bin/bash register: paperclip_config_update changed_when: "'changed' in paperclip_config_update.stdout" - name: Ensure Paperclip systemd service is installed ansible.builtin.copy: dest: /etc/systemd/system/{{ paperclip_service_name }}.service owner: root group: root mode: "0644" content: | [Unit] Description=Paperclip Server After=network-online.target Wants=network-online.target [Service] Type=simple User={{ paperclip_user }} Group={{ paperclip_user }} WorkingDirectory={{ paperclip_home }} Environment=HOME={{ paperclip_home }} Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ExecStart=/usr/bin/env npx -y paperclipai run Restart=always RestartSec=3 TimeoutStopSec=20 [Install] WantedBy=multi-user.target notify: Restart Paperclip - name: Ensure UFW allows Paperclip port when enabled community.general.ufw: rule: allow port: "{{ paperclip_port | string }}" proto: tcp when: ansible_os_family == "Debian" - name: Ensure Paperclip service is enabled and running ansible.builtin.systemd: name: "{{ paperclip_service_name }}" daemon_reload: true enabled: true state: started - name: Wait for Paperclip health endpoint ansible.builtin.uri: url: "http://{{ ansible_host }}:{{ paperclip_port }}/api/health" method: GET return_content: true register: paperclip_health retries: 10 delay: 3 until: paperclip_health.status == 200 - name: Show health response ansible.builtin.debug: var: paperclip_health.json handlers: - name: Restart Paperclip ansible.builtin.systemd: name: "{{ paperclip_service_name }}" daemon_reload: true state: restarted