feat(playwright): adopt per-thread sessions for shared Chrome automation

This commit is contained in:
2026-03-05 18:33:36 +08:00
parent aec5bcbfab
commit 8f9fc6d7f7
15 changed files with 180 additions and 78 deletions
+129 -51
View File
@@ -1,25 +1,34 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat >&2 <<'EOF'
Usage: tools/pw [--session <name>] <playwright-cli-command> [args...]
Session resolution priority:
1) --session
2) PLAYWRIGHT_SHARED_SESSION
3) PLAYWRIGHT_SESSION_OWNER
4) CODEX_THREAD_ID
5) codex-default
EOF
}
if [[ $# -lt 1 ]]; then
echo "Usage: tools/pw <playwright-cli-command> [args...]" >&2
usage
exit 1
fi
for arg in "$@"; do
if [[ "$arg" == "--session" || "$arg" == "--session="* ]]; then
echo "Do not pass --session directly. Use PLAYWRIGHT_SHARED_SESSION." >&2
exit 2
fi
done
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
PWCLI="${PWCLI:-$CODEX_HOME/skills/playwright/scripts/playwright_cli.sh}"
SESSION="${PLAYWRIGHT_SHARED_SESSION:-codex-shared}"
LOCK_TIMEOUT="${PLAYWRIGHT_SHARED_LOCK_TIMEOUT:-120}"
LOCK_DIR="${PLAYWRIGHT_SHARED_LOCK_DIR:-/tmp/pw-shared-session.lock}"
LOCK_ROOT="${PLAYWRIGHT_SHARED_LOCK_DIR_BASE:-/tmp/pw-session-locks}"
INIT_MODE="${PLAYWRIGHT_SHARED_INIT_MODE:-headed}"
if [[ ! -d "$LOCK_ROOT" ]]; then
mkdir -p "$LOCK_ROOT"
fi
if ! command -v npx >/dev/null 2>&1; then
echo "npx is required." >&2
exit 1
@@ -29,6 +38,70 @@ if [[ ! -x "$PWCLI" ]]; then
exit 1
fi
sanitize_token() {
local value="$1"
value="$(printf '%s' "$value" \
| tr '[:upper:]' '[:lower:]' \
| tr -cs 'a-z0-9._-' '-' \
| sed -e 's/^-*//' -e 's/-*$//')"
if [[ -z "$value" ]]; then
value="default"
fi
printf '%s' "$value"
}
derive_session() {
if [[ -n "${PLAYWRIGHT_SHARED_SESSION:-}" ]]; then
printf '%s' "$PLAYWRIGHT_SHARED_SESSION"
return
fi
local owner="${PLAYWRIGHT_SESSION_OWNER:-${CODEX_THREAD_ID:-default}}"
local owner_hash
owner_hash="$(printf '%s' "$owner" | shasum -a 256 | awk '{print substr($1,1,12)}')"
if [[ -z "$owner_hash" ]]; then
owner_hash="default"
fi
printf 'codex-%s' "$owner_hash"
}
session_override=""
cmd=()
while [[ $# -gt 0 ]]; do
case "$1" in
--session)
if [[ $# -lt 2 ]]; then
echo "--session requires a value." >&2
exit 1
fi
session_override="$2"
shift 2
;;
--session=*)
session_override="${1#--session=}"
shift
;;
*)
cmd+=("$1")
shift
;;
esac
done
if [[ ${#cmd[@]} -lt 1 ]]; then
usage
exit 1
fi
SESSION="${session_override:-$(derive_session)}"
SAFE_SESSION="$(sanitize_token "$SESSION")"
LOCK_DIR="${LOCK_ROOT}/${SAFE_SESSION}.lock"
# Default to a single user-owned Chrome via CDP unless caller overrides.
: "${PLAYWRIGHT_MCP_CDP_ENDPOINT:=http://127.0.0.1:9222}"
: "${PLAYWRIGHT_MCP_ISOLATED:=false}"
export PLAYWRIGHT_MCP_CDP_ENDPOINT
export PLAYWRIGHT_MCP_ISOLATED
acquire_lock() {
local start_ts now lock_pid lock_mtime
start_ts="$(date +%s)"
@@ -72,9 +145,39 @@ is_missing_session_error() {
return 1
}
init_session() {
local init_code
set +e
if [[ "$INIT_MODE" == "headless" ]]; then
run_pw open about:blank >/dev/null 2>&1
init_code=$?
elif [[ "$INIT_MODE" == "headed" ]]; then
run_pw open about:blank --headed >/dev/null 2>&1
init_code=$?
if [[ $init_code -ne 0 ]]; then
run_pw open about:blank >/dev/null 2>&1
init_code=$?
fi
else
run_pw open about:blank --headed >/dev/null 2>&1
init_code=$?
if [[ $init_code -ne 0 ]]; then
run_pw open about:blank >/dev/null 2>&1
init_code=$?
fi
fi
set -e
if [[ $init_code -ne 0 ]]; then
cat >&2 <<EOF
Failed to initialize Playwright session '$SESSION'.
Ensure Chrome remote debugging is ready at ${PLAYWRIGHT_MCP_CDP_ENDPOINT}.
EOF
exit 1
fi
}
acquire_lock
cmd=("$@")
verb="${cmd[0]}"
if [[ "$verb" == "open" ]]; then
@@ -104,45 +207,20 @@ if [[ "$verb" == "open" ]]; then
fi
fi
if [[ "$verb" != "open" ]]; then
set +e
out="$(run_pw "${cmd[@]}" 2>&1)"
code=$?
set -e
if [[ $code -eq 0 ]]; then
echo "$out"
exit 0
fi
if is_missing_session_error "$out"; then
set +e
if [[ "$INIT_MODE" == "headless" ]]; then
run_pw open about:blank >/dev/null 2>&1
init_code=$?
elif [[ "$INIT_MODE" == "headed" ]]; then
run_pw open about:blank --headed >/dev/null 2>&1
init_code=$?
if [[ $init_code -ne 0 ]]; then
run_pw open about:blank >/dev/null 2>&1
init_code=$?
fi
else
run_pw open about:blank --headed >/dev/null 2>&1
init_code=$?
if [[ $init_code -ne 0 ]]; then
run_pw open about:blank >/dev/null 2>&1
init_code=$?
fi
fi
set -e
if [[ $init_code -ne 0 ]]; then
echo "Failed to initialize shared Playwright session." >&2
exit 1
fi
run_pw "${cmd[@]}"
exit 0
fi
echo "$out" >&2
exit $code
set +e
out="$(run_pw "${cmd[@]}" 2>&1)"
code=$?
set -e
if [[ $code -eq 0 ]]; then
echo "$out"
exit 0
fi
run_pw "${cmd[@]}"
if is_missing_session_error "$out"; then
init_session
run_pw "${cmd[@]}"
exit 0
fi
echo "$out" >&2
exit $code