From 5e3db5dfd510eb4dc93b017c2db454764f51af14 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Thu, 18 Jun 2026 14:51:06 +0800 Subject: [PATCH] feat: run opencode acp with launchd on macos --- .../acp_server_opencode/defaults/main.yml | 2 +- .../acp_server_opencode/tasks/config.yml | 13 ++++++ .../acp_server_opencode/tasks/macos.yml | 13 ++++++ .../templates/opencode.plist.j2 | 41 +++++++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 roles/vhosts/acp_server_opencode/tasks/macos.yml create mode 100644 roles/vhosts/acp_server_opencode/templates/opencode.plist.j2 diff --git a/roles/vhosts/acp_server_opencode/defaults/main.yml b/roles/vhosts/acp_server_opencode/defaults/main.yml index 3203bc5..edad010 100644 --- a/roles/vhosts/acp_server_opencode/defaults/main.yml +++ b/roles/vhosts/acp_server_opencode/defaults/main.yml @@ -18,7 +18,7 @@ acp_opencode_bridge_service_name: xworkmate-bridge acp_opencode_bridge_local_source_dir: "{{ playbook_dir }}/../xworkmate-bridge" acp_opencode_bridge_local_build_dir: "{{ playbook_dir }}/.artifacts/acp_opencode" acp_opencode_bridge_local_binary_path: "{{ acp_opencode_bridge_local_build_dir }}/xworkmate-go-core" -acp_opencode_bridge_build_goos: linux +acp_opencode_bridge_build_goos: "{{ 'darwin' if ansible_os_family == 'Darwin' else 'linux' }}" acp_opencode_bridge_build_goarch: "{{ 'arm64' if ansible_architecture in ['aarch64', 'arm64'] else 'amd64' }}" acp_opencode_bridge_binary_path: /usr/local/bin/xworkmate-go-core acp_opencode_bridge_use_prebuilt: "{{ lookup('ansible.builtin.env', 'AI_WORKSPACE_USE_PREBUILT_BRIDGE') | default('false', true) | bool }}" diff --git a/roles/vhosts/acp_server_opencode/tasks/config.yml b/roles/vhosts/acp_server_opencode/tasks/config.yml index 7b9b3bc..e41ec9e 100644 --- a/roles/vhosts/acp_server_opencode/tasks/config.yml +++ b/roles/vhosts/acp_server_opencode/tasks/config.yml @@ -32,6 +32,7 @@ ansible.builtin.command: cmd: chattr -i "{{ acp_opencode_bridge_binary_path }}" when: + - ansible_os_family != 'Darwin' - "'i' in (acp_opencode_bridge_binary_attrs.stdout | default(''))" changed_when: true become: true @@ -49,6 +50,7 @@ ansible.builtin.command: cmd: chattr +i "{{ acp_opencode_bridge_binary_path }}" when: + - ansible_os_family != 'Darwin' - "'i' in (acp_opencode_bridge_binary_attrs.stdout | default(''))" changed_when: true become: true @@ -89,11 +91,13 @@ register: acp_opencode_service_attrs changed_when: false failed_when: false + when: ansible_os_family != 'Darwin' - name: Remove immutable flag from OpenCode ACP systemd service when present ansible.builtin.command: cmd: chattr -i "/etc/systemd/system/{{ acp_opencode_service_name }}.service" when: + - ansible_os_family != 'Darwin' - "'i' in (acp_opencode_service_attrs.stdout | default(''))" changed_when: true become: true @@ -106,11 +110,13 @@ group: root mode: "0644" notify: Restart acp opencode + when: ansible_os_family != 'Darwin' - name: Restore immutable flag on OpenCode ACP systemd service ansible.builtin.command: cmd: chattr +i "/etc/systemd/system/{{ acp_opencode_service_name }}.service" when: + - ansible_os_family != 'Darwin' - "'i' in (acp_opencode_service_attrs.stdout | default(''))" changed_when: true become: true @@ -118,6 +124,7 @@ - name: Reload systemd manager configuration ansible.builtin.systemd: daemon_reload: true + when: ansible_os_family != 'Darwin' - name: Ensure Caddy is enabled and running ansible.builtin.systemd: @@ -125,6 +132,7 @@ enabled: true state: started when: + - ansible_os_family != 'Darwin' - acp_opencode_manage_caddy | bool - name: Ensure OpenCode ACP service is enabled and running @@ -134,3 +142,8 @@ state: started when: - not ansible_check_mode + - ansible_os_family != 'Darwin' + +- name: Import macOS specific OpenCode ACP tasks + ansible.builtin.import_tasks: macos.yml + when: ansible_os_family == 'Darwin' diff --git a/roles/vhosts/acp_server_opencode/tasks/macos.yml b/roles/vhosts/acp_server_opencode/tasks/macos.yml new file mode 100644 index 0000000..780a618 --- /dev/null +++ b/roles/vhosts/acp_server_opencode/tasks/macos.yml @@ -0,0 +1,13 @@ +--- +- name: Create launchd plist template for OpenCode ACP + ansible.builtin.template: + src: opencode.plist.j2 + dest: "{{ ansible_env.HOME }}/Library/LaunchAgents/plus.svc.xworkspace.acp.opencode.plist" + mode: "0644" + notify: Restart acp opencode on macOS + +- name: Reload launchd agent for OpenCode ACP + ansible.builtin.command: "launchctl load -w {{ ansible_env.HOME }}/Library/LaunchAgents/plus.svc.xworkspace.acp.opencode.plist" + register: launchctl_result + changed_when: false + failed_when: launchctl_result.rc != 0 and 'already loaded' not in launchctl_result.stderr diff --git a/roles/vhosts/acp_server_opencode/templates/opencode.plist.j2 b/roles/vhosts/acp_server_opencode/templates/opencode.plist.j2 new file mode 100644 index 0000000..0f10855 --- /dev/null +++ b/roles/vhosts/acp_server_opencode/templates/opencode.plist.j2 @@ -0,0 +1,41 @@ + + + + + Label + plus.svc.xworkspace.acp.opencode + ProgramArguments + + /bin/bash + -c + + export PATH="{{ acp_opencode_path }}" + export LITELLM_MASTER_KEY="{{ acp_opencode_auth_token }}" + {% for key, value in acp_opencode_environment.items() %} + export {{ key }}="{{ value }}" + {% endfor %} + + exec "{{ acp_opencode_bridge_binary_path }}" acp-server \ + --port {{ acp_opencode_listen_port }} \ + --host {{ acp_opencode_listen_host }} \ + --sub-command opencode \ + --sub-command mcp-app-server + + + RunAtLoad + + KeepAlive + + WorkingDirectory + {{ acp_opencode_workdir }} + StandardOutPath + {{ ansible_env.HOME }}/.local/state/xworkspace/acp.opencode.log + StandardErrorPath + {{ ansible_env.HOME }}/.local/state/xworkspace/acp.opencode.err.log + EnvironmentVariables + + HOME + {{ acp_opencode_workdir }} + + +