playbooks/setup-xworkspace-console.yaml
2026-06-14 09:27:59 +08:00

693 lines
28 KiB
YAML

---
- name: Setup AI Agentic Workspace runtime
hosts: "{{ xworkspace_console_hosts | default('all') }}"
become: true
gather_facts: true
vars:
xworkspace_console_user: ubuntu
xworkspace_console_public_access: "{{ true if ai_workspace_security_level | default('standard') != 'strict' else false }}"
xworkspace_console_domain: workspace.svc.plus
xworkspace_console_home: /home/ubuntu
xworkspace_console_root: /home/ubuntu/xworkspace
xworkspace_console_repo_dir: /home/ubuntu/xworkspace-console
xworkspace_console_dashboard_dir: /home/ubuntu/xworkspace-console/dashboard
xworkspace_console_api_dir: /home/ubuntu/xworkspace-console/api
xworkspace_console_scripts_dir: /home/ubuntu/xworkspace/scripts
xworkspace_console_config_dir: /home/ubuntu/.config/xworkspace
xworkspace_console_url: http://127.0.0.1:17000
xworkspace_console_port: 17000
xworkspace_console_api_port: 8788
xworkspace_console_ttyd_port: 7681
xworkspace_console_enable_ttyd: true
xworkspace_console_install_chrome: true
xworkspace_console_autostart_enabled: true
xworkspace_console_ttyd_binary_path: /usr/local/bin/ttyd
ai_workspace_auth_token: "{{ lookup('ansible.builtin.env', 'AI_WORKSPACE_AUTH_TOKEN') | default('', true) }}"
xworkspace_console_auth_token: >-
{{ lookup('ansible.builtin.env', 'XWORKSPACE_CONSOLE_AUTH_TOKEN')
| default(lookup('ansible.builtin.env', 'AI_WORKSPACE_AUTH_TOKEN'), true)
| default(lookup('ansible.builtin.env', 'BRIDGE_AUTH_TOKEN'), true)
| default(lookup('ansible.builtin.env', 'XWORKMATE_BRIDGE_AUTH_TOKEN'), true)
| default(lookup('ansible.builtin.env', 'INTERNAL_SERVICE_TOKEN'), true) }}
xworkspace_console_review_auth_token: "{{ lookup('ansible.builtin.env', 'BRIDGE_REVIEW_AUTH_TOKEN') | default('', true) }}"
xworkspace_console_portal_services:
- key: litellm
name: LiteLLM Admin UI
url: http://localhost:4000/ui
openMode: iframe
healthUrl: http://127.0.0.1:4000/ui
description: Model routing and provider administration.
icon: chart
match:
- litellm
- lite
port: 4000
role: model-router
- key: openclaw
name: OpenClaw
url: http://127.0.0.1:18789/channels
openMode: external
healthUrl: http://127.0.0.1:18789/channels
description: Gateway dashboard.
icon: claw
match:
- openclaw
- gateway
port: 18789
role: gateway
- key: vault
name: Vault Server
url: http://127.0.0.1:8200/ui
openMode: external
healthUrl: http://127.0.0.1:8200/ui
description: Vault UI.
icon: shield
match:
- vault
port: 8200
- key: terminal
name: Terminal
url: http://127.0.0.1:7681
openMode: iframe
healthUrl: http://127.0.0.1:7681
description: Local ttyd terminal.
icon: terminal
match:
- ttyd
- terminal
port: 7681
tasks:
- name: Install Google Chrome apt repository prerequisites
ansible.builtin.apt:
name:
- ca-certificates
- curl
- gnupg
- xdg-utils
state: present
install_recommends: false
update_cache: true
- name: Ensure Google Chrome apt keyring directory exists
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
owner: root
group: root
mode: "0755"
- name: Install Google Linux signing key
ansible.builtin.shell: |
set -euo pipefail
tmp="$(mktemp)"
curl -fsSL "https://dl.google.com/linux/linux_signing_key.pub" -o "$tmp"
gpg --dearmor -o /etc/apt/keyrings/google-linux-signing-key.gpg "$tmp"
rm -f "$tmp"
chmod 0644 /etc/apt/keyrings/google-linux-signing-key.gpg
args:
executable: /bin/bash
creates: /etc/apt/keyrings/google-linux-signing-key.gpg
- name: Configure Google Chrome apt repository
ansible.builtin.copy:
dest: /etc/apt/sources.list.d/google-chrome.list
owner: root
group: root
mode: "0644"
content: "deb [arch=amd64 signed-by=/etc/apt/keyrings/google-linux-signing-key.gpg] https://dl.google.com/linux/chrome/deb/ stable main\n"
- name: Refresh apt cache after Google Chrome repository changes
ansible.builtin.apt:
update_cache: true
- name: Install AI Agentic Workspace runtime packages
ansible.builtin.apt:
update_cache: true
name:
- xfce4
- python3
- golang-go
- google-chrome-stable
state: present
- name: Ensure ttyd binary target directory exists
ansible.builtin.file:
path: "{{ xworkspace_console_ttyd_binary_path | dirname }}"
state: directory
owner: root
group: root
mode: "0755"
- name: Set ttyd binary download metadata
ansible.builtin.set_fact:
xworkspace_console_ttyd_arch: >-
{{ 'x86_64' if ansible_architecture in ['x86_64', 'amd64'] else
'aarch64' if ansible_architecture in ['aarch64', 'arm64'] else
'' }}
- name: Fail when ttyd binary architecture is unsupported
ansible.builtin.fail:
msg: "Unsupported architecture for ttyd binary: {{ ansible_architecture }}"
when: xworkspace_console_ttyd_arch == ''
- name: Download ttyd release binary
ansible.builtin.get_url:
url: "https://github.com/tsl0922/ttyd/releases/latest/download/ttyd.{{ xworkspace_console_ttyd_arch }}"
dest: "{{ xworkspace_console_ttyd_binary_path }}"
mode: "0755"
owner: root
group: root
force: true
- name: Verify ttyd binary
ansible.builtin.command: "{{ xworkspace_console_ttyd_binary_path }} --version"
register: xworkspace_console_ttyd_version
changed_when: false
- name: Show ttyd binary version
ansible.builtin.debug:
var: xworkspace_console_ttyd_version.stdout
- name: Ensure AI Agentic Workspace user exists
ansible.builtin.user:
name: "{{ xworkspace_console_user }}"
state: present
create_home: true
shell: /bin/bash
when: xworkspace_console_user != 'root'
- name: Ensure AI Agentic Workspace directories exist
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0755"
loop:
- "{{ xworkspace_console_root }}"
- "{{ xworkspace_console_scripts_dir }}"
- "{{ xworkspace_console_repo_dir }}"
- "{{ xworkspace_console_home }}/.config"
- "{{ xworkspace_console_config_dir }}"
- "{{ xworkspace_console_home }}/.config/autostart"
- "{{ xworkspace_console_home }}/.config/systemd"
- "{{ xworkspace_console_home }}/.config/systemd/user"
- "{{ xworkspace_console_home }}/.config/systemd/user/default.target.wants"
- "{{ xworkspace_console_home }}/.config/systemd/user/timers.target.wants"
- name: Deploy AI Agentic Workspace status generator
ansible.builtin.copy:
dest: "{{ xworkspace_console_scripts_dir }}/generate-status.py"
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0755"
content: |
#!/usr/bin/env python3
from __future__ import annotations
import datetime as dt
import json
import os
import re
import subprocess
import sys
from pathlib import Path
from urllib import request, error
HOME = Path("{{ xworkspace_console_home }}")
ROOT = Path("{{ xworkspace_console_root }}")
CONSOLE_DIR = Path("{{ xworkspace_console_dashboard_dir }}")
LITELLM_CONFIG = HOME / ".local/share/xworkspace/litellm-config.yaml"
OUTPUT = CONSOLE_DIR / "public/status.json"
def run(*cmd: str) -> str:
try:
return subprocess.check_output(cmd, text=True, stderr=subprocess.DEVNULL).strip()
except Exception:
return ""
def systemd_state(unit: str, scope: str = "--user") -> str:
cmd = ["systemctl"]
if scope == "--user":
cmd.append("--user")
cmd += ["is-active", unit]
return run(*cmd) or "unknown"
def health(url: str) -> tuple[str, str]:
try:
with request.urlopen(url, timeout=2) as response:
payload = response.read().decode("utf-8", "replace").strip()
return ("active" if response.status < 400 else "degraded", payload[:160] or "ok")
except error.HTTPError as exc:
return ("degraded", f"HTTP {exc.code}")
except Exception as exc:
return ("inactive", str(exc).splitlines()[0][:160])
def probe_unit(name: str, label: str, group: str, endpoint: str | None = None, candidates: list[str] | None = None, scope: str = "--user") -> dict:
candidates = candidates or [name]
state = "unknown"
found = None
for candidate in candidates:
state = systemd_state(candidate, scope=scope)
if state != "unknown":
found = candidate
break
details = f"{label} {state}"
if endpoint:
endpoint_state, endpoint_detail = health(endpoint)
if endpoint_state == "active" and state in {"active", "unknown"}:
state = "active"
elif endpoint_state == "degraded" and state == "active":
state = "degraded"
details = endpoint_detail
return {
"id": found or name,
"name": found or name,
"label": label,
"state": state,
"details": details,
"endpoint": endpoint,
"group": group,
}
def parse_models(path: Path) -> list[dict]:
if not path.exists():
return []
text = path.read_text(encoding="utf-8", errors="replace")
models = []
current = None
for line in text.splitlines():
match = re.search(r"^\s*model_name:\s*(.+?)\s*$", line)
if match:
current = match.group(1).strip().strip('"\'')
models.append({
"name": current,
"state": "active",
"details": "Detected from litellm-config.yaml",
})
return models
services = [
probe_unit("xworkspace-console.service", "console", "core", "{{ xworkspace_console_url }}", ["xworkspace-console.service"], "--user"),
probe_unit("xworkspace-litellm.service", "litellm", "core", "http://127.0.0.1:4000/health/liveliness", ["xworkspace-litellm.service", "litellm.service"], "--user"),
probe_unit("xworkmate-bridge.service", "xworkmate-bridge", "workspace", None, ["xworkmate-bridge.service"], "--user"),
probe_unit("openclaw-gateway.service", "openclaw", "workspace", None, ["openclaw-gateway.service", "openclaw.service"], "--user"),
probe_unit("hermes-gateway.service", "hermes", "workspace", None, ["hermes-gateway.service", "hermes.service"], "--user"),
probe_unit("qmd.service", "QMD", "workspace", None, ["qmd.service", "QMD.service", "xworkspace-qmd.service"], "--user"),
probe_unit("xworkspace-ttyd.service", "ttyd", "workspace", "http://127.0.0.1:7681", ["xworkspace-ttyd.service"], "--user"),
probe_unit("ttyd.service", "ttyd(system)", "workspace", "http://127.0.0.1:7681", ["ttyd.service"], "system"),
]
litellm = next((item for item in services if item["label"] == "litellm"), None)
terminal = next((item for item in services if item["label"].startswith("ttyd")), None)
console = next((item for item in services if item["label"] == "console"), None)
output = {
"generatedAt": dt.datetime.now(dt.timezone.utc).isoformat(),
"summary": "Workspace healthy" if all(item["state"] == "active" for item in services if item["label"] != "QMD") else "Workspace degraded",
"services": services,
"models": parse_models(LITELLM_CONFIG),
"terminalTranscript": "ubuntu@workspace:~$ openclaw status\nstatus snapshot generated from live probes",
"latencySummary": "live probe",
"latencySource": litellm["details"] if litellm else "no litellm probe",
"rpmSummary": "live probe",
"rpmSource": "xworkspace-litellm.service",
"costSummary": "live probe",
"costSource": "litellm-config.yaml",
"consoleState": console["state"] if console else "unknown",
"consoleDetails": console["details"] if console else "No console probe",
"terminalState": terminal["state"] if terminal else "unknown",
"terminalDetails": terminal["details"] if terminal else "No ttyd probe",
}
OUTPUT.parent.mkdir(parents=True, exist_ok=True)
tmp = OUTPUT.with_suffix(".json.tmp")
tmp.write_text(json.dumps(output, indent=2, sort_keys=True), encoding="utf-8")
os.replace(tmp, OUTPUT)
print(f"Wrote {OUTPUT}")
- name: Deploy AI Agentic Workspace status service
ansible.builtin.copy:
dest: "{{ xworkspace_console_home }}/.config/systemd/user/xworkspace-status.service"
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0644"
content: |
[Unit]
Description=AI Agentic Workspace status snapshot generator
After=xworkspace-console.service xworkspace-litellm.service
Wants=xworkspace-console.service xworkspace-litellm.service
[Service]
Type=oneshot
ExecStart={{ xworkspace_console_scripts_dir }}/generate-status.py
- name: Deploy AI Agentic Workspace status timer
ansible.builtin.copy:
dest: "{{ xworkspace_console_home }}/.config/systemd/user/xworkspace-status.timer"
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0644"
content: |
[Unit]
Description=Refresh AI Agentic Workspace status snapshot periodically
[Timer]
OnBootSec=5
OnUnitActiveSec=8
Unit=xworkspace-status.service
Persistent=true
[Install]
WantedBy=timers.target
- name: Deploy Xinit entrypoint for AI Agentic Workspace
ansible.builtin.copy:
dest: "{{ xworkspace_console_home }}/.xinitrc"
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0755"
content: |
#!/usr/bin/env bash
set -euo pipefail
export XDG_CURRENT_DESKTOP=XFCE
export DESKTOP_SESSION=xfce
export BROWSER={{ xworkspace_console_scripts_dir }}/chrome-app.sh
mkdir -p "$HOME/.config/xworkspace"
if [ -z "${XAUTHORITY:-}" ]; then
export XAUTHORITY="$HOME/.Xauthority"
fi
cat > "$HOME/.config/xworkspace/session.env" <<EOF
DISPLAY=${DISPLAY:-}
XAUTHORITY=${XAUTHORITY:-}
XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-}
DBUS_SESSION_BUS_ADDRESS=${DBUS_SESSION_BUS_ADDRESS:-}
EOF
systemctl --user import-environment DISPLAY XAUTHORITY XDG_RUNTIME_DIR DBUS_SESSION_BUS_ADDRESS >/dev/null 2>&1 || true
dbus-update-activation-environment --systemd DISPLAY XAUTHORITY XDG_RUNTIME_DIR DBUS_SESSION_BUS_ADDRESS >/dev/null 2>&1 || true
systemctl --user daemon-reload >/dev/null 2>&1 || true
systemctl --user start xworkspace-console.service >/dev/null 2>&1 || true
exec dbus-launch --exit-with-session startxfce4
- name: Point Xsession to Xinit entrypoint
ansible.builtin.copy:
dest: "{{ xworkspace_console_home }}/.xsession"
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0755"
content: |
#!/usr/bin/env bash
exec "$HOME/.xinitrc"
- name: Deploy AI Agentic Workspace console launcher script
ansible.builtin.copy:
dest: "{{ xworkspace_console_scripts_dir }}/start.sh"
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0755"
content: |
#!/usr/bin/env bash
set -euo pipefail
exit 0
- name: Deploy AI Agentic Workspace Chrome launcher wrapper
ansible.builtin.copy:
dest: "{{ xworkspace_console_scripts_dir }}/chrome-app.sh"
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0755"
content: |
#!/usr/bin/env bash
set -euo pipefail
mkdir -p "$HOME/.config/xworkspace-chrome"
exec /usr/bin/google-chrome \
--app=http://localhost:17000 \
--user-data-dir="$HOME/.config/xworkspace-chrome" \
--profile-directory=Default \
--no-first-run \
--disable-session-crashed-bubble \
--disable-sync \
--new-window
- name: Clone xworkspace-console repository
ansible.builtin.git:
repo: "https://github.com/ai-workspace-lab/xworkspace-console.git"
dest: "{{ xworkspace_console_repo_dir }}"
version: "main"
depth: 1
force: true
become_user: "{{ xworkspace_console_user }}"
- name: Build dashboard assets on target
ansible.builtin.shell: |
cd "{{ xworkspace_console_dashboard_dir }}"
npm install && npm run build
become_user: "{{ xworkspace_console_user }}"
- name: Deploy AI Workspace portal service configuration
ansible.builtin.copy:
dest: "{{ xworkspace_console_config_dir }}/portal-services.json"
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0644"
content: "{{ {'services': xworkspace_console_portal_services} | to_nice_json }}\n"
- name: Deploy AI Workspace portal token file
ansible.builtin.copy:
dest: "{{ xworkspace_console_config_dir }}/auth-token"
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0600"
content: "{{ xworkspace_console_auth_token }}\n"
no_log: true
- name: Deploy AI Workspace shared auth token file
ansible.builtin.copy:
dest: "{{ xworkspace_console_home }}/.ai_workspace_auth_token"
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0600"
content: "{{ xworkspace_console_auth_token }}\n"
no_log: true
- name: Deploy XWorkspace API environment
ansible.builtin.copy:
dest: "{{ xworkspace_console_config_dir }}/portal.env"
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0600"
content: |
AI_WORKSPACE_AUTH_TOKEN={{ xworkspace_console_auth_token }}
XWORKSPACE_CONSOLE_AUTH_TOKEN={{ xworkspace_console_auth_token }}
BRIDGE_AUTH_TOKEN={{ xworkspace_console_auth_token }}
BRIDGE_REVIEW_AUTH_TOKEN={{ xworkspace_console_review_auth_token }}
XWORKMATE_BRIDGE_AUTH_TOKEN={{ xworkspace_console_auth_token }}
INTERNAL_SERVICE_TOKEN={{ xworkspace_console_auth_token }}
XWORKSPACE_PORTAL_SERVICES_FILE={{ xworkspace_console_config_dir }}/portal-services.json
no_log: true
- name: Deploy XWorkspace Console service
ansible.builtin.copy:
dest: "{{ xworkspace_console_home }}/.config/systemd/user/xworkspace-console.service"
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0644"
content: |
[Unit]
Description=XWorkspace Console dashboard
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory={{ xworkspace_console_dashboard_dir }}
ExecStart=/usr/bin/npm run preview -- --host 127.0.0.1 --port {{ xworkspace_console_port }}
Restart=always
RestartSec=2
[Install]
WantedBy=default.target
- name: Deploy XWorkspace API service
ansible.builtin.copy:
dest: "{{ xworkspace_console_home }}/.config/systemd/user/xworkspace-api.service"
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0644"
content: |
[Unit]
Description=XWorkspace status API
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory={{ xworkspace_console_api_dir }}
EnvironmentFile={{ xworkspace_console_config_dir }}/portal.env
ExecStart=/usr/bin/env go run .
Restart=always
RestartSec=2
[Install]
WantedBy=default.target
- name: Deploy AI Agentic Workspace ttyd service
ansible.builtin.copy:
dest: "{{ xworkspace_console_home }}/.config/systemd/user/xworkspace-ttyd.service"
owner: "{{ xworkspace_console_user }}"
group: "{{ xworkspace_console_user }}"
mode: "0644"
content: |
[Unit]
Description=AI Agentic Workspace ttyd
After=graphical-session.target
Wants=graphical-session.target
[Service]
Type=simple
ExecStart={{ xworkspace_console_ttyd_binary_path }} -i lo -p {{ xworkspace_console_ttyd_port }} -O login bash
Restart=always
RestartSec=2
[Install]
WantedBy=default.target
- name: Enable XWorkspace Console service
ansible.builtin.file:
src: "{{ xworkspace_console_home }}/.config/systemd/user/xworkspace-console.service"
dest: "{{ xworkspace_console_home }}/.config/systemd/user/default.target.wants/xworkspace-console.service"
state: link
become_user: "{{ xworkspace_console_user }}"
- name: Enable XWorkspace API service
ansible.builtin.file:
src: "{{ xworkspace_console_home }}/.config/systemd/user/xworkspace-api.service"
dest: "{{ xworkspace_console_home }}/.config/systemd/user/default.target.wants/xworkspace-api.service"
state: link
become_user: "{{ xworkspace_console_user }}"
- name: Enable AI Agentic Workspace ttyd service
ansible.builtin.file:
src: "{{ xworkspace_console_home }}/.config/systemd/user/xworkspace-ttyd.service"
dest: "{{ xworkspace_console_home }}/.config/systemd/user/default.target.wants/xworkspace-ttyd.service"
state: link
become_user: "{{ xworkspace_console_user }}"
- name: Enable AI Agentic Workspace LiteLLM service
ansible.builtin.file:
src: "{{ xworkspace_console_home }}/.config/systemd/user/xworkspace-litellm.service"
dest: "{{ xworkspace_console_home }}/.config/systemd/user/default.target.wants/xworkspace-litellm.service"
state: link
force: true
become_user: "{{ xworkspace_console_user }}"
- name: Enable AI Agentic Workspace status timer
ansible.builtin.file:
src: "{{ xworkspace_console_home }}/.config/systemd/user/xworkspace-status.timer"
dest: "{{ xworkspace_console_home }}/.config/systemd/user/timers.target.wants/xworkspace-status.timer"
state: link
become_user: "{{ xworkspace_console_user }}"
- name: Kill legacy python http.server on port 7000
ansible.builtin.shell: |
pid=$(lsof -ti:7000 2>/dev/null || true)
if [ -n "$pid" ]; then
kill "$pid" 2>/dev/null || true
sleep 1
fi
become_user: "{{ xworkspace_console_user }}"
- name: Remove legacy portal chrome and target units
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- "{{ xworkspace_console_home }}/.config/systemd/user/xworkspace-portal.service"
- "{{ xworkspace_console_home }}/.config/systemd/user/xworkspace-chrome.service"
- "{{ xworkspace_console_home }}/.config/systemd/user/xworkspace-console.target"
- "{{ xworkspace_console_home }}/.config/systemd/user/default.target.wants/xworkspace-portal.service"
- "{{ xworkspace_console_home }}/.config/systemd/user/default.target.wants/xworkspace-chrome.service"
- "{{ xworkspace_console_home }}/.config/systemd/user/default.target.wants/xworkspace-console.target"
- name: Remove legacy portal directory
ansible.builtin.file:
path: "{{ xworkspace_console_root }}/portal"
state: absent
- name: Reload systemd user daemon
ansible.builtin.shell: |
uid="$(id -u {{ xworkspace_console_user }})"
loginctl enable-linger {{ xworkspace_console_user }} || true
systemctl start "user@${uid}.service" || true
runuser -u {{ xworkspace_console_user }} -- env XDG_RUNTIME_DIR="/run/user/${uid}" DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${uid}/bus" systemctl --user daemon-reload
become: true
- name: Restart xworkspace-console service
ansible.builtin.shell: |
uid="$(id -u {{ xworkspace_console_user }})"
loginctl enable-linger {{ xworkspace_console_user }} || true
systemctl start "user@${uid}.service" || true
runuser -u {{ xworkspace_console_user }} -- env XDG_RUNTIME_DIR="/run/user/${uid}" DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${uid}/bus" systemctl --user restart xworkspace-console.service
become: true
- name: Restart xworkspace-api service
ansible.builtin.shell: |
uid="$(id -u {{ xworkspace_console_user }})"
loginctl enable-linger {{ xworkspace_console_user }} || true
systemctl start "user@${uid}.service" || true
runuser -u {{ xworkspace_console_user }} -- env XDG_RUNTIME_DIR="/run/user/${uid}" DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${uid}/bus" systemctl --user restart xworkspace-api.service
become: true
- name: Restart xworkspace-ttyd service
ansible.builtin.shell: |
uid="$(id -u {{ xworkspace_console_user }})"
loginctl enable-linger {{ xworkspace_console_user }} || true
systemctl start "user@${uid}.service" || true
runuser -u {{ xworkspace_console_user }} -- env XDG_RUNTIME_DIR="/run/user/${uid}" DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${uid}/bus" systemctl --user restart xworkspace-ttyd.service
become: true
- name: Hide XFCE desktop icons
ansible.builtin.command: xfconf-query -c xfce4-desktop -p /desktop-icons/style -t int -s 0 --create
changed_when: true
failed_when: false
- name: Ensure Caddy fragment directory exists
ansible.builtin.file:
path: /etc/caddy/conf.d
state: directory
owner: root
group: root
mode: "0755"
- name: Deploy xworkspace-console public Caddy site
ansible.builtin.copy:
dest: "/etc/caddy/conf.d/{{ xworkspace_console_domain }}.caddy"
owner: root
group: root
mode: "0644"
content: |
{{ xworkspace_console_domain }} {
reverse_proxy 127.0.0.1:{{ xworkspace_console_port }}
}
when: xworkspace_console_public_access | bool
register: xworkspace_caddy_deploy
- name: Remove xworkspace-console public Caddy site when disabled
ansible.builtin.file:
path: "/etc/caddy/conf.d/{{ xworkspace_console_domain }}.caddy"
state: absent
when: not (xworkspace_console_public_access | bool)
register: xworkspace_caddy_remove
- name: Reload Caddy if xworkspace-console proxy changed
ansible.builtin.service:
name: caddy
state: reloaded
when: (xworkspace_caddy_deploy.changed or xworkspace_caddy_remove.changed) and not ansible_check_mode
failed_when: false