Files
heicode-win/cc-haha/desktop/scripts/e2e-install-center-agent-browser.sh
T
gongzhiyong 1efcf73992 chore: consolidate heicode workspace into single repository
Import cc-haha and new-api into a single repository layout for unified delivery.

Made-with: Cursor
2026-04-29 21:08:15 +08:00

169 lines
5.2 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
DESKTOP_DIR="$ROOT_DIR/desktop"
if ! command -v agent-browser >/dev/null 2>&1; then
echo "agent-browser is required but was not found in PATH" >&2
exit 1
fi
PORTS="$(bun -e 'import { createServer } from "node:net"; const getPort = () => new Promise((resolve) => { const server = createServer(); server.listen(0, "127.0.0.1", () => { const address = server.address(); const port = typeof address === "object" && address ? address.port : 0; server.close(() => resolve(port)); }); }); const apiPort = await getPort(); const webPort = await getPort(); console.log(`${apiPort} ${webPort}`);')"
API_PORT="${PORTS%% *}"
WEB_PORT="${PORTS##* }"
BASE_URL="http://127.0.0.1:${API_PORT}"
WEB_URL="http://127.0.0.1:${WEB_PORT}/?serverUrl=${BASE_URL}"
RUN_ID="$(date +%s)-$RANDOM"
SESSION_NAME="cc-haha-install-e2e-${RUN_ID}"
ARTIFACT_DIR="$(mktemp -d "/tmp/cc-haha-install-e2e-${RUN_ID}-XXXX")"
SERVER_LOG="${ARTIFACT_DIR}/server.log"
WEB_LOG="${ARTIFACT_DIR}/web.log"
if [[ "${CC_HAHA_E2E_USE_REAL_CONFIG:-1}" == "1" ]]; then
CONFIG_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
else
CONFIG_DIR="${CLAUDE_CONFIG_DIR:-${ARTIFACT_DIR}/claude-config}"
mkdir -p "${CONFIG_DIR}"
fi
cleanup() {
local exit_code=$?
if [[ -n "${SERVER_PID:-}" ]]; then
kill "${SERVER_PID}" >/dev/null 2>&1 || true
wait "${SERVER_PID}" >/dev/null 2>&1 || true
fi
if [[ -n "${WEB_PID:-}" ]]; then
kill "${WEB_PID}" >/dev/null 2>&1 || true
wait "${WEB_PID}" >/dev/null 2>&1 || true
fi
agent-browser --session "${SESSION_NAME}" close >/dev/null 2>&1 || true
if [[ $exit_code -ne 0 ]]; then
echo "Artifacts kept at: ${ARTIFACT_DIR}" >&2
else
rm -rf "${ARTIFACT_DIR}"
fi
}
trap cleanup EXIT
echo "Starting isolated backend on ${BASE_URL}"
(
cd "${ROOT_DIR}"
CLAUDE_CONFIG_DIR="${CONFIG_DIR}" SERVER_PORT="${API_PORT}" bun run src/server/index.ts --host 127.0.0.1 --port "${API_PORT}"
) >"${SERVER_LOG}" 2>&1 &
SERVER_PID=$!
echo "Starting web UI on http://127.0.0.1:${WEB_PORT}"
(
cd "${DESKTOP_DIR}"
bun run dev -- --host 127.0.0.1 --port "${WEB_PORT}"
) >"${WEB_LOG}" 2>&1 &
WEB_PID=$!
wait_for_http() {
local url="$1"
for _ in $(seq 1 120); do
if curl -fsS "$url" >/dev/null 2>&1; then
return 0
fi
sleep 1
done
echo "Timed out waiting for ${url}" >&2
return 1
}
browser_body() {
agent-browser --session "${SESSION_NAME}" get text body
}
wait_for_body_contains() {
local needle="$1"
local attempts="${2:-180}"
for _ in $(seq 1 "${attempts}"); do
if browser_body | grep -Fq "${needle}"; then
return 0
fi
sleep 2
done
echo "Timed out waiting for page text: ${needle}" >&2
return 1
}
wait_for_body_contains_any() {
local attempts="$1"
shift
for _ in $(seq 1 "${attempts}"); do
local body
body="$(browser_body)"
for needle in "$@"; do
if grep -Fq "${needle}" <<<"${body}"; then
return 0
fi
done
sleep 2
done
echo "Timed out waiting for any expected page text: $*" >&2
return 1
}
wait_for_path() {
local path="$1"
local attempts="${2:-180}"
for _ in $(seq 1 "${attempts}"); do
if [[ -e "${path}" ]]; then
return 0
fi
sleep 2
done
echo "Timed out waiting for path: ${path}" >&2
return 1
}
AB="agent-browser --session ${SESSION_NAME}"
TELEGRAM_PROMPT="请根据 https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/telegram 安装 Telegram 官方插件。如果 URL 对应官方 external_plugins,请直接识别 plugin id 并完成用户级安装,然后告诉我结果。"
SKILL_PROMPT="请根据 https://www.aitmpl.com/component/skill/creative-design/ui-ux-pro-max 这个 skill 页面安装 ui-ux-pro-max。如果页面里有官方安装命令,请直接识别并执行,然后告诉我结果。"
PLUGIN_DIR="${CONFIG_DIR}/plugins/cache/claude-plugins-official/telegram"
SKILL_DIR="${CONFIG_DIR}/skills/ui-ux-pro-max"
wait_for_http "${BASE_URL}/health"
wait_for_http "http://127.0.0.1:${WEB_PORT}"
${AB} open "${WEB_URL}"
${AB} click 'button:has-text("Settings")'
${AB} click 'button:has-text("Install")'
${AB} screenshot "${ARTIFACT_DIR}/01-install-center.png" >/dev/null
run_install_prompt() {
local prompt="$1"
${AB} click 'button:has-text("New install chat")'
${AB} fill 'textarea' "${prompt}"
${AB} click 'button:has-text("Send request")'
}
echo "Running plugin install flow"
run_install_prompt "${TELEGRAM_PROMPT}"
wait_for_body_contains 'telegram@claude-plugins-official'
wait_for_path "${PLUGIN_DIR}"
${AB} click 'button:has-text("Open Plugins")'
wait_for_body_contains 'Enabled1'
wait_for_body_contains 'telegram Enabled'
${AB} screenshot "${ARTIFACT_DIR}/02-plugin-installed.png" >/dev/null
echo "Running skill install flow"
${AB} click 'button:has-text("Install")'
run_install_prompt "${SKILL_PROMPT}"
wait_for_body_contains 'claude-code-templates@latest'
wait_for_path "${SKILL_DIR}"
${AB} click 'button:has-text("Open Skills")'
wait_for_body_contains 'Installed Skills'
wait_for_body_contains 'ui-ux-pro-max'
${AB} screenshot "${ARTIFACT_DIR}/03-skill-installed.png" >/dev/null
echo "Install Center E2E passed"
echo "Config dir: ${CONFIG_DIR}"
echo "API port: ${API_PORT}"
echo "Web port: ${WEB_PORT}"