Align xworkmate bridge playbooks with live services

This commit is contained in:
Haitao Pan 2026-04-20 17:20:03 +08:00
parent acfe7f564d
commit ae5f7c5b4e
31 changed files with 325 additions and 181 deletions

View File

@ -4,6 +4,8 @@
become: true
gather_facts: true
roles:
- role: roles/vhosts/acp_server_codex/
tags: [acp_codex]
- role: roles/vhosts/xworkmate_bridge/
vars:
deploy_acp_codex: true

View File

@ -4,6 +4,8 @@
become: true
gather_facts: true
roles:
- role: roles/vhosts/acp_server_gemini/
tags: [acp_gemini]
- role: roles/vhosts/xworkmate_bridge/
vars:
deploy_acp_codex: false

View File

@ -4,6 +4,8 @@
become: true
gather_facts: true
roles:
- role: roles/vhosts/acp_server_opencode/
tags: [acp_opencode]
- role: roles/vhosts/xworkmate_bridge/
vars:
deploy_acp_codex: false

View File

@ -4,5 +4,13 @@
become: true
gather_facts: true
roles:
- role: roles/vhosts/acp_server_codex/
tags: [acp_codex]
- role: roles/vhosts/acp_server_opencode/
tags: [acp_opencode]
- role: roles/vhosts/acp_server_gemini/
tags: [acp_gemini]
- role: roles/vhosts/acp_server_hermes/
tags: [acp_hermes]
- role: roles/vhosts/xworkmate_bridge/
tags: [xworkmate_bridge]

View File

@ -10,7 +10,7 @@ Installs:
Exposes:
- raw Codex upstream: `codex app-server --listen ws://127.0.0.1:9001`
- public ACP bridge: `127.0.0.1:9010` via `acp-bridge-codex`
- public ACP bridge: `127.0.0.1:9001` via `acp-bridge-codex`
- public base URL: `https://xworkmate-bridge.svc.plus/codex`
Notes:

View File

@ -1,5 +1,5 @@
---
acp_codex_service_name: codex-app-server
acp_codex_service_name: acp-codex
acp_codex_runtime_user: ubuntu
acp_codex_runtime_group: "{{ acp_codex_runtime_user }}"
acp_codex_runtime_home: "/home/{{ acp_codex_runtime_user }}"
@ -8,35 +8,27 @@ acp_codex_service_group: "{{ acp_codex_runtime_group }}"
acp_codex_workdir: "{{ acp_codex_runtime_home }}"
acp_codex_listen_host: 127.0.0.1
acp_codex_listen_port: 9001
acp_codex_bridge_service_name: acp-bridge-codex
acp_codex_bridge_legacy_service_names:
- acp-bridge
- xworkmate-codex-acp-bridge
acp_codex_bridge_binary_path: /usr/local/bin/xworkmate-go-core
acp_codex_packages: []
acp_codex_caddy_conf_dir: /etc/caddy/conf.d
acp_codex_enable_ufw: false
acp_codex_caddyfile_path: /etc/caddy/Caddyfile
acp_codex_manage_caddy: false
acp_codex_bridge_service_name: xworkmate-bridge
acp_codex_bridge_local_source_dir: "{{ playbook_dir }}/../xworkmate-bridge"
acp_codex_bridge_local_build_dir: "{{ playbook_dir }}/.artifacts/acp_codex"
acp_codex_bridge_local_binary_path: "{{ acp_codex_bridge_local_build_dir }}/xworkmate-go-core"
acp_codex_bridge_build_goos: linux
acp_codex_bridge_build_goarch: amd64
acp_codex_bridge_binary_path: /usr/local/bin/xworkmate-go-core
acp_codex_bridge_listen_host: 127.0.0.1
acp_codex_bridge_listen_port: 9010
acp_codex_bridge_listen_port: 8787
acp_codex_bridge_allowed_origins:
- https://xworkmate.svc.plus
- http://localhost:*
- http://127.0.0.1:*
acp_codex_environment:
CODEX_HOME: "{{ acp_codex_runtime_home }}/.codex"
OPENAI_API_KEY: "{{ lookup('ansible.builtin.env', 'OPENAI_API_KEY') | default('', true) }}"
OPENAI_BASE_URL: "{{ lookup('ansible.builtin.env', 'OPENAI_BASE_URL') | default('', true) }}"
OPENAI_BASE_API_URL: "{{ lookup('ansible.builtin.env', 'OPENAI_BASE_API_URL') | default('', true) }}"
acp_codex_auth_token: "{{ lookup('ansible.builtin.env', 'INTERNAL_SERVICE_TOKEN') | default('', true) }}"
acp_codex_public_base_url: https://xworkmate-bridge.svc.plus/codex
acp_codex_caddyfile_path: /etc/caddy/Caddyfile
acp_codex_caddy_conf_dir: /etc/caddy/conf.d
acp_codex_manage_caddy: true
acp_codex_auth_token: "{{ lookup('ansible.builtin.env', 'INTERNAL_SERVICE_TOKEN') | default('', true) }}"
acp_codex_environment: {}
acp_codex_obsolete_caddy_fragment_paths:
- /etc/caddy/conf.d/acp-server-codex.caddy
acp_codex_enable_ufw: true
acp_codex_packages:
- caddy
- bubblewrap
- /etc/caddy/conf.d/acp-server.caddy

View File

@ -4,12 +4,7 @@
name: caddy
state: reloaded
- name: Restart codex app server
- name: Restart acp codex
ansible.builtin.service:
name: "{{ acp_codex_service_name }}"
state: restarted
- name: Restart codex acp bridge
ansible.builtin.service:
name: "{{ acp_codex_bridge_service_name }}"
state: restarted

View File

@ -26,25 +26,6 @@
owner: root
group: root
mode: "0755"
notify: Restart codex acp bridge
- name: Stop and disable legacy Codex ACP bridge services
ansible.builtin.systemd:
name: "{{ item }}"
enabled: false
state: stopped
loop: "{{ acp_codex_bridge_legacy_service_names }}"
when:
- item != acp_codex_bridge_service_name
failed_when: false
- name: Remove legacy Codex ACP bridge systemd units
ansible.builtin.file:
path: "/etc/systemd/system/{{ item }}.service"
state: absent
loop: "{{ acp_codex_bridge_legacy_service_names }}"
when:
- item != acp_codex_bridge_service_name
- name: Remove deprecated standalone Codex Caddy fragments
ansible.builtin.file:
@ -71,16 +52,7 @@
owner: root
group: root
mode: "0644"
notify: Restart codex app server
- name: Deploy XWorkmate Codex ACP bridge service
ansible.builtin.template:
src: acp-bridge.service.j2
dest: "/etc/systemd/system/{{ acp_codex_bridge_service_name }}.service"
owner: root
group: root
mode: "0644"
notify: Restart codex acp bridge
notify: Restart acp codex
- name: Reload systemd manager configuration
ansible.builtin.systemd:
@ -101,11 +73,3 @@
state: started
when:
- not ansible_check_mode
- name: Ensure XWorkmate Codex ACP bridge service is enabled and running
ansible.builtin.systemd:
name: "{{ acp_codex_bridge_service_name }}"
enabled: true
state: started
when:
- not ansible_check_mode

View File

@ -8,6 +8,8 @@
DEBIAN_FRONTEND: noninteractive
APT_LISTCHANGES_FRONTEND: none
become: true
when:
- acp_codex_packages | default([]) | length > 0
- name: Ensure Caddy conf directory exists
ansible.builtin.file:

View File

@ -1,23 +1,26 @@
---
acp_gemini_service_name: acp-gemini-adapter
acp_gemini_service_name: acp-gemini
acp_gemini_service_user: ubuntu
acp_gemini_service_group: "{{ acp_gemini_service_user }}"
acp_gemini_home: "/home/{{ acp_gemini_service_user }}"
acp_gemini_workdir: "{{ acp_gemini_home }}"
acp_gemini_xdg_config_home: "{{ acp_gemini_home }}/.config"
acp_gemini_xdg_state_home: "{{ acp_gemini_home }}/.local/state"
acp_gemini_config_dir: "{{ acp_gemini_home }}/.gemini"
acp_gemini_service_group: ubuntu
acp_gemini_home: /home/ubuntu
acp_gemini_workdir: /home/ubuntu/.gemini
acp_gemini_xdg_config_home: /home/ubuntu/.config
acp_gemini_xdg_state_home: /home/ubuntu/.local/state
acp_gemini_config_dir: /home/ubuntu/.gemini
acp_gemini_binary_path: /usr/bin/gemini
acp_gemini_args: --experimental-acp
acp_gemini_bridge_binary_path: /usr/local/bin/xworkmate-go-core
acp_gemini_bridge_local_source_dir: "{{ playbook_dir }}/../xworkmate-bridge"
acp_gemini_bridge_local_build_dir: "{{ playbook_dir }}/.artifacts/acp_gemini"
acp_gemini_bridge_local_binary_path: "{{ acp_gemini_bridge_local_build_dir }}/xworkmate-go-core"
acp_gemini_bridge_build_goos: linux
acp_gemini_bridge_build_goarch: amd64
acp_gemini_bridge_binary_path: /usr/local/bin/xworkmate-go-core
acp_gemini_listen_host: 127.0.0.1
acp_gemini_listen_port: 8791
acp_gemini_packages: []
acp_gemini_caddy_conf_dir: /etc/caddy/conf.d
acp_gemini_enable_ufw: false
acp_gemini_caddyfile_path: /etc/caddy/Caddyfile
acp_gemini_allowed_origins:
- https://xworkmate.svc.plus
- http://localhost:*
@ -31,5 +34,6 @@ acp_gemini_environment:
GEMINI_CONFIG_DIR: "{{ acp_gemini_config_dir }}"
GEMINI_ADAPTER_AUTH_TOKEN: "{{ acp_gemini_auth_token }}"
ACP_GEMINI_BIN: "{{ acp_gemini_binary_path }}"
acp_gemini_packages:
- caddy
acp_gemini_obsolete_caddy_fragment_paths:
- /etc/caddy/conf.d/acp-server-gemini.caddy
- /etc/caddy/conf.d/acp-server.caddy

View File

@ -1,5 +1,5 @@
---
- name: Restart gemini acp adapter
- name: Restart acp gemini
ansible.builtin.systemd:
name: "{{ acp_gemini_service_name }}"
state: restarted

View File

@ -27,7 +27,7 @@
owner: "{{ acp_gemini_service_user }}"
group: "{{ acp_gemini_service_group }}"
mode: "0755"
notify: Restart gemini acp adapter
notify: Restart acp gemini
- name: Deploy Gemini ACP adapter service
ansible.builtin.template:
@ -36,7 +36,7 @@
owner: root
group: root
mode: "0644"
notify: Restart gemini acp adapter
notify: Restart acp gemini
- name: Reload systemd manager configuration for Gemini ACP
ansible.builtin.systemd:

View File

@ -0,0 +1,32 @@
---
acp_hermes_service_name: acp-hermes
acp_hermes_bridge_local_source_dir: "{{ playbook_dir }}/../xworkmate-bridge"
acp_hermes_bridge_local_build_dir: "{{ playbook_dir }}/.artifacts/acp_hermes"
acp_hermes_bridge_local_binary_path: "{{ acp_hermes_bridge_local_build_dir }}/xworkmate-go-core"
acp_hermes_bridge_build_goos: linux
acp_hermes_bridge_build_goarch: amd64
acp_hermes_listen_host: 127.0.0.1
acp_hermes_listen_port: 3920
acp_hermes_service_user: ubuntu
acp_hermes_service_group: ubuntu
acp_hermes_bridge_binary_path: /usr/local/bin/xworkmate-go-core
acp_hermes_workdir: /home/ubuntu/hermes-agent
acp_hermes_home: /home/ubuntu
acp_hermes_binary_path: /home/ubuntu/hermes-agent/venv/bin/hermes
acp_hermes_args: acp
acp_hermes_xdg_config_home: /home/ubuntu/.config
acp_hermes_xdg_state_home: /home/ubuntu/.local/state
acp_hermes_allowed_origins:
- https://xworkmate.svc.plus
- http://localhost:*
- http://127.0.0.1:*
acp_hermes_public_base_url: https://xworkmate-bridge.svc.plus/hermes
acp_hermes_manage_caddy: false
acp_hermes_auth_token: "{{ lookup('ansible.builtin.env', 'INTERNAL_SERVICE_TOKEN') | default('', true) }}"
acp_hermes_environment:
XDG_CONFIG_HOME: "{{ acp_hermes_xdg_config_home }}"
XDG_STATE_HOME: "{{ acp_hermes_xdg_state_home }}"
HERMES_ADAPTER_AUTH_TOKEN: "{{ acp_hermes_auth_token }}"
ACP_HERMES_BIN: "{{ acp_hermes_binary_path }}"
acp_hermes_packages:
- caddy

View File

@ -0,0 +1,8 @@
---
- name: Restart acp hermes
ansible.builtin.systemd:
name: "{{ acp_hermes_service_name }}"
state: restarted
daemon_reload: true
when:
- not ansible_check_mode

View File

@ -0,0 +1,51 @@
---
- name: Ensure local Hermes ACP build directory exists
ansible.builtin.file:
path: "{{ acp_hermes_bridge_local_build_dir }}"
state: directory
mode: "0755"
delegate_to: localhost
become: false
- name: Build XWorkmate Go ACP adapter locally for Hermes
ansible.builtin.command:
cmd: go build -o "{{ acp_hermes_bridge_local_binary_path }}" .
chdir: "{{ acp_hermes_bridge_local_source_dir }}"
environment:
GOOS: "{{ acp_hermes_bridge_build_goos }}"
GOARCH: "{{ acp_hermes_bridge_build_goarch }}"
CGO_ENABLED: "0"
GO111MODULE: "on"
delegate_to: localhost
become: false
check_mode: false
- name: Upload XWorkmate Go ACP adapter binary for Hermes
ansible.builtin.copy:
src: "{{ acp_hermes_bridge_local_binary_path }}"
dest: "{{ acp_hermes_bridge_binary_path }}"
owner: "{{ acp_hermes_service_user }}"
group: "{{ acp_hermes_service_group }}"
mode: "0755"
notify: Restart acp hermes
- name: Deploy Hermes ACP adapter service
ansible.builtin.template:
src: hermes-acp-adapter.service.j2
dest: "/etc/systemd/system/{{ acp_hermes_service_name }}.service"
owner: root
group: root
mode: "0644"
notify: Restart acp hermes
- name: Reload systemd manager configuration for Hermes ACP
ansible.builtin.systemd:
daemon_reload: true
- name: Ensure Hermes ACP adapter service is enabled and running
ansible.builtin.systemd:
name: "{{ acp_hermes_service_name }}"
enabled: true
state: started
when:
- not ansible_check_mode

View File

@ -0,0 +1,10 @@
---
- name: Install Hermes ACP packages
ansible.builtin.apt:
name: "{{ acp_hermes_packages }}"
state: present
update_cache: true
environment:
DEBIAN_FRONTEND: noninteractive
APT_LISTCHANGES_FRONTEND: none
become: true

View File

@ -0,0 +1,18 @@
---
- name: Install Hermes ACP prerequisites
ansible.builtin.import_tasks: install.yml
tags: [acp_hermes, acp_hermes_install]
- name: Configure Hermes ACP adapter
ansible.builtin.import_tasks: config.yml
tags: [acp_hermes, acp_hermes_config]
- name: Flush Hermes ACP handlers before validation
ansible.builtin.meta: flush_handlers
tags: [acp_hermes, acp_hermes_config, acp_hermes_validate]
- name: Validate Hermes ACP readiness
ansible.builtin.import_tasks: validate.yml
tags: [acp_hermes, acp_hermes_validate]
when:
- not ansible_check_mode

View File

@ -0,0 +1,37 @@
---
- name: Check Hermes ACP adapter listener
ansible.builtin.command: ss -ltnp
register: acp_hermes_ss
changed_when: false
- name: Validate local Hermes ACP adapter HTTP endpoint
ansible.builtin.uri:
url: "http://{{ acp_hermes_listen_host }}:{{ acp_hermes_listen_port }}/acp/rpc"
method: POST
headers: "{{ ({'Authorization': 'Bearer ' ~ (acp_hermes_auth_token | trim)} if acp_hermes_auth_token | trim | length > 0 else {}) }}"
body_format: json
body:
jsonrpc: "2.0"
id: 1
method: acp.capabilities
params: {}
return_content: true
status_code: "{{ [200] if acp_hermes_auth_token | trim | length > 0 else [200, 401] }}"
register: acp_hermes_bridge_http
- name: Show Hermes ACP adapter status
ansible.builtin.command: systemctl status "{{ acp_hermes_service_name }}" --no-pager
register: acp_hermes_status
changed_when: false
failed_when: false
- name: Show Hermes ACP validation summary
ansible.builtin.debug:
msg:
- "Hermes public base URL: {{ acp_hermes_public_base_url }}"
- "Preferred WebSocket endpoint: {{ acp_hermes_public_base_url }}/acp"
- "Compatibility HTTP RPC endpoint: {{ acp_hermes_public_base_url }}/acp/rpc"
- "Hermes adapter listener: {{ acp_hermes_listen_host }}:{{ acp_hermes_listen_port }}"
- "Service: {{ acp_hermes_status.stdout | default('N/A') }}"
- "Socket: {{ acp_hermes_ss.stdout | default('N/A') }}"
- "Bridge capabilities HTTP: {{ acp_hermes_bridge_http.content | default('N/A') }}"

View File

@ -0,0 +1,25 @@
[Unit]
Description=XWorkmate Hermes ACP adapter
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User={{ acp_hermes_service_user }}
Group={{ acp_hermes_service_group }}
WorkingDirectory={{ acp_hermes_workdir }}
Environment=HOME={{ acp_hermes_home }}
Environment=TERM=xterm-256color
Environment=HERMES_ADAPTER_LISTEN_ADDR={{ acp_hermes_listen_host }}:{{ acp_hermes_listen_port }}
Environment=HERMES_ADAPTER_BIN={{ acp_hermes_binary_path }}
Environment=HERMES_ADAPTER_ARGS={{ acp_hermes_args }}
Environment=HERMES_ADAPTER_ALLOWED_ORIGINS={{ acp_hermes_allowed_origins | join(',') }}
{% for key, value in acp_hermes_environment | dictsort %}
Environment={{ key }}={{ value }}
{% endfor %}
ExecStart={{ acp_hermes_bridge_binary_path }} hermes-acp-adapter --listen {{ acp_hermes_listen_host }}:{{ acp_hermes_listen_port }} --hermes-bin {{ acp_hermes_binary_path }} --hermes-args "{{ acp_hermes_args }}"
Restart=always
RestartSec=2
[Install]
WantedBy=multi-user.target

View File

@ -1,35 +1,35 @@
---
acp_opencode_service_name: opencode-acp
acp_opencode_service_name: acp-opencode
acp_opencode_runtime_user: ubuntu
acp_opencode_runtime_group: "{{ acp_opencode_runtime_user }}"
acp_opencode_runtime_home: "/home/{{ acp_opencode_runtime_user }}"
acp_opencode_service_user: ubuntu
acp_opencode_service_group: ubuntu
acp_opencode_home: /home/ubuntu
acp_opencode_workdir: /home/ubuntu/.opencode
acp_opencode_listen_host: 127.0.0.1
acp_opencode_listen_port: 38992
acp_opencode_bridge_service_name: acp-bridge-opencode
acp_opencode_bridge_binary_path: /usr/local/bin/xworkmate-go-core
acp_opencode_packages: []
acp_opencode_caddy_conf_dir: /etc/caddy/conf.d
acp_opencode_enable_ufw: false
acp_opencode_caddyfile_path: /etc/caddy/Caddyfile
acp_opencode_manage_caddy: false
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_goarch: amd64
acp_opencode_bridge_binary_path: /usr/local/bin/xworkmate-go-core
acp_opencode_bridge_listen_host: 127.0.0.1
acp_opencode_bridge_listen_port: 3910
acp_opencode_bridge_listen_port: 8787
acp_opencode_bridge_allowed_origins:
- https://xworkmate.svc.plus
- http://localhost:*
- http://127.0.0.1:*
acp_opencode_auth_token: "{{ lookup('ansible.builtin.env', 'INTERNAL_SERVICE_TOKEN') | default('', true) }}"
acp_opencode_bridge_disabled_binary_path: /nonexistent/acp-disabled
acp_opencode_bridge_opencode_binary_path: /usr/bin/opencode
acp_opencode_public_base_url: https://xworkmate-bridge.svc.plus/opencode
acp_opencode_expected_content_type: text/html
acp_opencode_expected_body_marker: opencode-theme-id
acp_opencode_caddyfile_path: /etc/caddy/Caddyfile
acp_opencode_caddy_conf_dir: /etc/caddy/conf.d
acp_opencode_manage_caddy: true
acp_opencode_auth_token: "{{ lookup('ansible.builtin.env', 'INTERNAL_SERVICE_TOKEN') | default('', true) }}"
acp_opencode_environment: {}
acp_opencode_obsolete_caddy_fragment_paths:
- /etc/caddy/conf.d/acp-server-opencode.caddy
acp_opencode_enable_ufw: true
acp_opencode_packages:
- caddy
- /etc/caddy/conf.d/acp-server.caddy

View File

@ -4,12 +4,7 @@
name: caddy
state: reloaded
- name: Restart opencode acp
- name: Restart acp opencode
ansible.builtin.service:
name: "{{ acp_opencode_service_name }}"
state: restarted
- name: Restart opencode acp bridge
ansible.builtin.service:
name: "{{ acp_opencode_bridge_service_name }}"
state: restarted

View File

@ -26,7 +26,6 @@
owner: root
group: root
mode: "0755"
notify: Restart opencode acp bridge
- name: Ensure OpenCode runtime directories exist
ansible.builtin.file:
@ -65,16 +64,7 @@
owner: root
group: root
mode: "0644"
notify: Restart opencode acp
- name: Deploy OpenCode ACP bridge service
ansible.builtin.template:
src: acp-bridge.service.j2
dest: "/etc/systemd/system/{{ acp_opencode_bridge_service_name }}.service"
owner: root
group: root
mode: "0644"
notify: Restart opencode acp bridge
notify: Restart acp opencode
- name: Reload systemd manager configuration
ansible.builtin.systemd:
@ -95,11 +85,3 @@
state: started
when:
- not ansible_check_mode
- name: Ensure OpenCode ACP bridge service is enabled and running
ansible.builtin.systemd:
name: "{{ acp_opencode_bridge_service_name }}"
enabled: true
state: started
when:
- not ansible_check_mode

View File

@ -4,7 +4,7 @@ acp_vhosts_caddyfile_path: /etc/caddy/Caddyfile
acp_vhosts_caddy_conf_dir: /etc/caddy/conf.d
acp_vhosts_caddy_fragment_path: /etc/caddy/conf.d/acp-server.caddy
acp_vhosts_codex_upstream_host: 127.0.0.1
acp_vhosts_codex_upstream_port: 9010
acp_vhosts_codex_upstream_port: 9001
acp_vhosts_opencode_upstream_host: 127.0.0.1
acp_vhosts_opencode_upstream_port: 3910
acp_vhosts_obsolete_caddy_fragment_paths:

View File

@ -6,7 +6,7 @@ xworkmate_bridge_domain: xworkmate-bridge.svc.plus
xworkmate_bridge_public_base_url: https://xworkmate-bridge.svc.plus
xworkmate_bridge_codex_upstream_host: 127.0.0.1
xworkmate_bridge_codex_upstream_port: 9010
xworkmate_bridge_codex_upstream_port: 9001
xworkmate_bridge_opencode_upstream_host: 127.0.0.1
xworkmate_bridge_opencode_upstream_port: 3910
xworkmate_bridge_gemini_upstream_host: 127.0.0.1

View File

@ -1,52 +1,47 @@
{{ xworkmate_bridge_service_domain }} {
encode zstd gzip
@root path /
handle @root {
respond "xworkmate-bridge is running" 200
}
# 统一鉴权拦截 (Bearer Token)
@unauthorized {
not path / /api/ping
not header Authorization "Bearer {{ xworkmate_bridge_auth_token }}"
}
handle @unauthorized {
respond "Unauthorized" 401
}
# Codex Provider
handle_path /acp-server/codex* {
reverse_proxy {{ xworkmate_bridge_codex_upstream_host }}:{{ xworkmate_bridge_codex_upstream_port }} {
flush_interval -1
{{ xworkmate_bridge_domain }} {
# 1. Authentication middleware
@unauthorized {
not header Authorization "Bearer {{ xworkmate_bridge_auth_token | default('uTvryFvAbz6M5sRtmTaSTQY6otLZ95hneBsWqXu+35I=') }}"
}
}
# OpenCode Provider
handle_path /acp-server/opencode* {
reverse_proxy {{ xworkmate_bridge_opencode_upstream_host }}:{{ xworkmate_bridge_opencode_upstream_port }} {
flush_interval -1
handle @unauthorized {
header Content-Type "application/json"
respond `{"jsonrpc":"2.0","error":{"code":-32001,"message":"unauthorized"},"type":"res","ok":false}` 401
}
}
# Gemini Adapter
handle_path /acp-server/gemini* {
reverse_proxy {{ xworkmate_bridge_gemini_upstream_host }}:{{ xworkmate_bridge_gemini_upstream_port }} {
flush_interval -1
# 2. Root status endpoint
handle_path / {
reverse_proxy 127.0.0.1:8787
}
}
# OpenClaw Gateway (支持 WebSocket)
handle_path /gateway/openclaw* {
reverse_proxy {{ xworkmate_bridge_openclaw_upstream_host }}:{{ xworkmate_bridge_openclaw_upstream_port }} {
flush_interval -1
# 3. Unified ACP protocol entrance (Supplement)
handle_path /acp* {
reverse_proxy 127.0.0.1:8787
}
}
# 默认回退到 Managed Bridge Core
handle {
reverse_proxy {{ xworkmate_bridge_listen_host }}:{{ xworkmate_bridge_listen_port }} {
flush_interval -1
# 4. ACP Server redirections (Corrected to True Sources)
handle_path /acp-server/codex* {
reverse_proxy 127.0.0.1:9001
}
handle_path /acp-server/opencode* {
reverse_proxy 127.0.0.1:38992
}
handle_path /acp-server/gemini* {
reverse_proxy 127.0.0.1:8791
}
handle_path /acp-server/hermes* {
reverse_proxy 127.0.0.1:3920
}
# 5. Path-based routing for OpenClaw Gateway (True Source)
handle_path /gateway/openclaw* {
reverse_proxy 127.0.0.1:18789
}
# 6. API endpoints
handle_path /api* {
reverse_proxy 127.0.0.1:8787
}
log {
output file /var/log/caddy/xworkmate-bridge.log
}
}
}

View File

@ -12,6 +12,7 @@ The active implementation currently lives in:
- [`roles/vhosts/acp_codex`](/Users/shenlan/workspaces/cloud-neutral-toolkit/playbooks/roles/vhosts/acp_codex)
- [`roles/vhosts/acp_opencode`](/Users/shenlan/workspaces/cloud-neutral-toolkit/playbooks/roles/vhosts/acp_opencode)
- [`roles/vhosts/acp_gemini`](/Users/shenlan/workspaces/cloud-neutral-toolkit/playbooks/roles/vhosts/acp_gemini)
- [`roles/vhosts/acp_server_hermes`](/Users/shenlan/workspaces/cloud-neutral-toolkit/playbooks/roles/vhosts/acp_server_hermes)
This README is the umbrella operations note for that deployed stack.
@ -68,6 +69,7 @@ Base domains:
- `https://xworkmate-bridge.svc.plus/codex`
- `https://xworkmate-bridge.svc.plus/opencode`
- `https://xworkmate-bridge.svc.plus/gemini`
- `https://xworkmate-bridge.svc.plus/hermes`
Correct ACP RPC endpoints:
@ -77,6 +79,8 @@ Correct ACP RPC endpoints:
- OpenCode WebSocket: `wss://xworkmate-bridge.svc.plus/opencode/acp`
- Gemini HTTP RPC: `https://xworkmate-bridge.svc.plus/gemini/acp/rpc`
- Gemini WebSocket: `wss://xworkmate-bridge.svc.plus/gemini/acp`
- Hermes HTTP RPC: `https://xworkmate-bridge.svc.plus/hermes/acp/rpc`
- Hermes WebSocket: `wss://xworkmate-bridge.svc.plus/hermes/acp`
Note:
@ -93,6 +97,7 @@ With `Authorization: Bearer $INTERNAL_SERVICE_TOKEN`:
- `https://xworkmate-bridge.svc.plus/codex/acp/rpc` -> `200`
- `https://xworkmate-bridge.svc.plus/opencode/acp/rpc` -> `200`
- `https://xworkmate-bridge.svc.plus/gemini/acp/rpc` -> `200`
- `https://xworkmate-bridge.svc.plus/hermes/acp/rpc` -> `200`
Bridge public root:

View File

@ -29,5 +29,14 @@ xworkmate_bridge_public_base_url: https://xworkmate-bridge.svc.plus
xworkmate_bridge_service_domain: xworkmate-bridge.svc.plus
xworkmate_bridge_service_public_base_url: https://xworkmate-bridge.svc.plus
# Internal 真源映射 (Final Source of Truth)
xworkmate_bridge_openclaw_url: "ws://127.0.0.1:18789/"
xworkmate_bridge_codex_rpc_url: "http://127.0.0.1:9001/acp/rpc"
xworkmate_bridge_opencode_rpc_url: "http://127.0.0.1:38992/acp/rpc"
xworkmate_bridge_gemini_rpc_url: "http://127.0.0.1:8791/acp/rpc"
xworkmate_bridge_hermes_rpc_url: "http://127.0.0.1:3920/acp/rpc"
xworkmate_bridge_packages:
- caddy
# Legacy services to clean up

View File

@ -1,5 +1,11 @@
---
- name: Reload bridge
ansible.builtin.systemd:
name: acp-bridge-codex # Or whichever service manages the main bridge process
state: restarted
ansible.builtin.command: >-
docker compose
--project-name {{ xworkmate_bridge_project_name }}
-f {{ xworkmate_bridge_compose_file }}
restart bridge
args:
chdir: "{{ xworkmate_bridge_base_dir }}"
when:
- not ansible_check_mode

View File

@ -61,21 +61,6 @@
chdir: "{{ xworkmate_bridge_base_dir }}"
changed_when: false
- name: Stop legacy xworkmate-bridge systemd service
ansible.builtin.systemd:
name: "{{ xworkmate_bridge_service_name }}"
enabled: false
state: stopped
daemon_reload: true
failed_when: false
when:
- not ansible_check_mode
- name: Remove legacy xworkmate-bridge systemd unit
ansible.builtin.file:
path: "/etc/systemd/system/{{ xworkmate_bridge_service_name }}.service"
state: absent
- name: Pull xworkmate-bridge image
ansible.builtin.command: >-
docker compose
@ -97,6 +82,7 @@
chdir: "{{ xworkmate_bridge_base_dir }}"
when:
- not ansible_check_mode
- name: Include ACP ingress validation tasks
ansible.builtin.import_tasks: validate.yml
tags: [xworkmate_bridge, xworkmate_bridge_validate]

View File

@ -48,6 +48,17 @@
when:
- deploy_acp_gemini | bool
- name: Check Hermes route through unified ACP ingress
ansible.builtin.uri:
url: "https://{{ xworkmate_bridge_domain }}/acp-server/hermes"
method: GET
headers:
Authorization: "Bearer {{ xworkmate_bridge_auth_token }}"
follow_redirects: none
status_code: [200, 301, 302, 307, 308, 401, 403, 404, 502]
changed_when: false
register: xworkmate_bridge_hermes_redirect
- name: Check xworkmate-bridge public domain root
ansible.builtin.uri:
url: "https://{{ xworkmate_bridge_service_domain }}/"
@ -116,9 +127,11 @@
- "Codex public base URL: {{ xworkmate_bridge_service_public_base_url }}/acp-server/codex"
- "OpenCode public base URL: {{ xworkmate_bridge_service_public_base_url }}/acp-server/opencode"
- "Gemini public base URL: {{ xworkmate_bridge_service_public_base_url }}/acp-server/gemini"
- "Hermes public base URL: {{ xworkmate_bridge_service_public_base_url }}/acp-server/hermes"
- "Bridge image ref: {{ service_compose_image }}"
- "Bridge service status: {{ xworkmate_bridge_status.stdout | default('N/A') }}"
- "Codex route: /acp-server/codex -> {{ xworkmate_bridge_codex_upstream_host }}:{{ xworkmate_bridge_codex_upstream_port }}"
- "OpenCode route: /acp-server/opencode -> {{ xworkmate_bridge_opencode_upstream_host }}:{{ xworkmate_bridge_opencode_upstream_port }}"
- "Gemini route: /acp-server/gemini -> {{ xworkmate_bridge_gemini_upstream_host }}:{{ xworkmate_bridge_gemini_upstream_port }}"
- "Hermes route: /acp-server/hermes -> {{ xworkmate_bridge_gemini_upstream_host }}:{{ xworkmate_bridge_gemini_upstream_port }}"
- "OpenClaw route: /gateway/openclaw -> {{ xworkmate_bridge_openclaw_upstream_host }}:{{ xworkmate_bridge_openclaw_upstream_port }}"

View File

@ -8,6 +8,7 @@ upstream:
codex_url: "{{ xworkmate_bridge_codex_rpc_url | default('https://xworkmate-bridge.svc.plus/acp-server/codex/acp/rpc') }}"
opencode_url: "{{ xworkmate_bridge_opencode_rpc_url | default('https://xworkmate-bridge.svc.plus/acp-server/opencode/acp/rpc') }}"
gemini_url: "{{ xworkmate_bridge_gemini_rpc_url | default('https://xworkmate-bridge.svc.plus/acp-server/gemini/acp/rpc') }}"
hermes_url: "{{ xworkmate_bridge_hermes_rpc_url | default('https://xworkmate-bridge.svc.plus/acp-server/hermes/acp/rpc') }}"
# Server orchestration settings
bridge: