From 50c2d85a14c354f316c1a220fb13f4252721f4a8 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Sat, 27 Jun 2026 08:56:52 +0800 Subject: [PATCH] fix(installer): keep macOS OpenClaw plugin on stable path --- scripts/patch-macos-playbooks.py | 52 +++++++++++++++++++-- scripts/setup-ai-workspace-all-in-one.sh | 12 ++--- tests/setup-ai-workspace-all-in-one-test.sh | 37 +++++++++++++++ 3 files changed, 91 insertions(+), 10 deletions(-) diff --git a/scripts/patch-macos-playbooks.py b/scripts/patch-macos-playbooks.py index c5a9aef..54ee2dc 100755 --- a/scripts/patch-macos-playbooks.py +++ b/scripts/patch-macos-playbooks.py @@ -675,17 +675,61 @@ def main(): " become_user: \"{{ gateway_openclaw_service_user }}\"\n" " when: ansible_os_family == 'Darwin'\n" "\n" - "- name: Link openclaw-multi-session-plugins to extensions (macOS)\n" + "- name: Inspect installed openclaw-multi-session-plugins path (macOS)\n" + " ansible.builtin.stat:\n" + " path: \"{{ gateway_openclaw_home }}/.openclaw/extensions/openclaw-multi-session-plugins\"\n" + " follow: false\n" + " register: openclaw_plugin_extension_stat_macos\n" + " when: ansible_os_family == 'Darwin'\n" + "\n" + "- name: Remove legacy temporary plugin symlink (macOS)\n" " ansible.builtin.file:\n" - " src: \"{{ gateway_openclaw_multi_session_plugin_dir | default('/tmp/openclaw-multi-session-plugins') }}\"\n" - " dest: \"{{ gateway_openclaw_home }}/.openclaw/extensions/openclaw-multi-session-plugins\"\n" - " state: link\n" + " path: \"{{ gateway_openclaw_home }}/.openclaw/extensions/openclaw-multi-session-plugins\"\n" + " state: absent\n" + " when:\n" + " - ansible_os_family == 'Darwin'\n" + " - openclaw_plugin_extension_stat_macos.stat.islnk | default(false)\n" + " notify: Restart openclaw\n" + "\n" + "- name: Ensure stable openclaw-multi-session-plugins directory (macOS)\n" + " ansible.builtin.file:\n" + " path: \"{{ gateway_openclaw_home }}/.openclaw/extensions/openclaw-multi-session-plugins\"\n" + " state: directory\n" " owner: \"{{ gateway_openclaw_service_user }}\"\n" " group: \"{{ gateway_openclaw_service_group }}\"\n" + " mode: \"0755\"\n" + " when: ansible_os_family == 'Darwin'\n" + " notify: Restart openclaw\n" + "\n" + "- name: Copy built openclaw-multi-session-plugins into stable directory (macOS)\n" + " ansible.builtin.copy:\n" + " src: \"{{ gateway_openclaw_multi_session_plugin_dir | default('/tmp/openclaw-multi-session-plugins') }}/{{ item }}\"\n" + " dest: \"{{ gateway_openclaw_home }}/.openclaw/extensions/openclaw-multi-session-plugins/\"\n" + " remote_src: true\n" + " owner: \"{{ gateway_openclaw_service_user }}\"\n" + " group: \"{{ gateway_openclaw_service_group }}\"\n" + " mode: preserve\n" + " loop:\n" + " - dist\n" + " - openclaw.plugin.json\n" + " - package.json\n" " become_user: \"{{ gateway_openclaw_service_user }}\"\n" " when: ansible_os_family == 'Darwin'\n" " notify: Restart openclaw\n" "\n" + "- name: Record stable openclaw-multi-session-plugins install (macOS)\n" + " ansible.builtin.command:\n" + " cmd: >-\n" + " {{ gateway_openclaw_binary_path }} plugins install\n" + " {{ (gateway_openclaw_home ~ '/.openclaw/extensions/openclaw-multi-session-plugins') | quote }} --force\n" + " environment:\n" + " HOME: \"{{ gateway_openclaw_home }}\"\n" + " PATH: \"{{ gateway_openclaw_service_path }}\"\n" + " OPENCLAW_NO_RESPAWN: \"1\"\n" + " become_user: \"{{ gateway_openclaw_service_user }}\"\n" + " changed_when: false\n" + " when: ansible_os_family == 'Darwin'\n" + "\n" ) if anchor in text and "Clone openclaw-multi-session-plugins repository (macOS)" not in text: text = text.replace(anchor, injected + anchor, 1) diff --git a/scripts/setup-ai-workspace-all-in-one.sh b/scripts/setup-ai-workspace-all-in-one.sh index 68167b5..28cdc14 100755 --- a/scripts/setup-ai-workspace-all-in-one.sh +++ b/scripts/setup-ai-workspace-all-in-one.sh @@ -2017,12 +2017,12 @@ append_var "LITELLM_SOURCE_REPO" "litellm_source_repo" append_var "LITELLM_VERSION" "litellm_version" append_var "OPENCLAW_MULTI_SESSION_PLUGIN_PACKAGE_SPEC" "gateway_openclaw_multi_session_plugin_package_spec" -append_var "DEEPSEEK_API_KEY" "litellm_deepseek_api_key" -append_var "NVIDIA_API_KEY" "litellm_nvidia_api_key" -append_var "OLLAMA_API_KEY" "litellm_ollama_api_key" -append_var "GEMINI_API_KEY" "litellm_gemini_api_key" -append_var "OPENAI_API_KEY" "litellm_openai_api_key" -append_var "ANTHROPIC_API_KEY" "litellm_anthropic_api_key" +append_secret_var "litellm_deepseek_api_key" "${DEEPSEEK_API_KEY:-}" +append_secret_var "litellm_nvidia_api_key" "${NVIDIA_API_KEY:-}" +append_secret_var "litellm_ollama_api_key" "${OLLAMA_API_KEY:-}" +append_secret_var "litellm_gemini_api_key" "${GEMINI_API_KEY:-}" +append_secret_var "litellm_openai_api_key" "${OPENAI_API_KEY:-}" +append_secret_var "litellm_anthropic_api_key" "${ANTHROPIC_API_KEY:-}" # 4. Resolve one auth token for the bridge and downstream service UIs/APIs. UNIFIED_AUTH_TOKEN="$(resolve_unified_auth_token)" diff --git a/tests/setup-ai-workspace-all-in-one-test.sh b/tests/setup-ai-workspace-all-in-one-test.sh index f47a4f4..77ee9cf 100755 --- a/tests/setup-ai-workspace-all-in-one-test.sh +++ b/tests/setup-ai-workspace-all-in-one-test.sh @@ -217,6 +217,39 @@ test_linux_identity_vars_can_be_overridden() ( printf '%s\n' "${ANSIBLE_EXTRA_VARS[@]}" | grep -q '^xworkspace_console_repo_dir=/srv/deploy/xworkspace-console$' || fail "console repo extra var missing" ) +test_provider_api_keys_use_secret_logging() { + local env_name ansible_var + while read -r env_name ansible_var; do + grep -Fq "append_secret_var \"$ansible_var\" \"\${$env_name:-}\"" "$BOOTSTRAP" || + fail "$env_name is not passed through the masked secret logger" + if grep -Fq "append_var \"$env_name\"" "$BOOTSTRAP"; then + fail "$env_name is still passed through the plain-text parameter logger" + fi + done <<'EOF' +DEEPSEEK_API_KEY litellm_deepseek_api_key +NVIDIA_API_KEY litellm_nvidia_api_key +OLLAMA_API_KEY litellm_ollama_api_key +GEMINI_API_KEY litellm_gemini_api_key +OPENAI_API_KEY litellm_openai_api_key +ANTHROPIC_API_KEY litellm_anthropic_api_key +EOF +} + +test_macos_plugin_patch_uses_stable_directory() { + local patcher="$SCRIPT_DIR/../scripts/patch-macos-playbooks.py" + grep -Fq 'Remove legacy temporary plugin symlink (macOS)' "$patcher" || + fail "macOS plugin patch does not migrate the legacy temporary symlink" + grep -Fq 'Ensure stable openclaw-multi-session-plugins directory (macOS)' "$patcher" || + fail "macOS plugin patch does not create a stable extension directory" + grep -Fq 'Copy built openclaw-multi-session-plugins into stable directory (macOS)' "$patcher" || + fail "macOS plugin patch does not copy the built plugin into stable storage" + grep -Fq 'Record stable openclaw-multi-session-plugins install (macOS)' "$patcher" || + fail "macOS plugin patch does not record stable OpenClaw provenance" + if grep -Fq 'Link openclaw-multi-session-plugins to extensions (macOS)' "$patcher"; then + fail "macOS plugin patch still installs the extension as a temporary symlink" + fi +} + test_root_does_not_require_sudo printf 'ok - root execution does not require sudo\n' test_non_root_uses_sudo @@ -246,3 +279,7 @@ test_linux_non_root_uses_current_user_home printf 'ok - Linux non-root deployment uses passwd home\n' test_linux_identity_vars_can_be_overridden printf 'ok - Linux deployment identity can be overridden\n' +test_provider_api_keys_use_secret_logging +printf 'ok - provider API keys use masked secret logging\n' +test_macos_plugin_patch_uses_stable_directory +printf 'ok - macOS plugin patch uses stable extension storage\n'