227 lines
4.9 KiB
Bash
Executable File
227 lines
4.9 KiB
Bash
Executable File
#!/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
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
|
PWCLI="${PWCLI:-$CODEX_HOME/skills/playwright/scripts/playwright_cli.sh}"
|
|
LOCK_TIMEOUT="${PLAYWRIGHT_SHARED_LOCK_TIMEOUT:-120}"
|
|
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
|
|
fi
|
|
if [[ ! -x "$PWCLI" ]]; then
|
|
echo "Playwright wrapper not found or not executable: $PWCLI" >&2
|
|
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)"
|
|
while ! mkdir "$LOCK_DIR" 2>/dev/null; do
|
|
lock_pid=""
|
|
if [[ -f "$LOCK_DIR/pid" ]]; then
|
|
lock_pid="$(cat "$LOCK_DIR/pid" 2>/dev/null || true)"
|
|
fi
|
|
if [[ -z "$lock_pid" ]]; then
|
|
lock_mtime="$(stat -f %m "$LOCK_DIR" 2>/dev/null || echo 0)"
|
|
now="$(date +%s)"
|
|
if (( now - lock_mtime >= 5 )); then
|
|
rm -rf "$LOCK_DIR" 2>/dev/null || true
|
|
continue
|
|
fi
|
|
fi
|
|
if [[ -n "$lock_pid" ]] && ! kill -0 "$lock_pid" 2>/dev/null; then
|
|
rm -rf "$LOCK_DIR" 2>/dev/null || true
|
|
continue
|
|
fi
|
|
now="$(date +%s)"
|
|
if (( now - start_ts >= LOCK_TIMEOUT )); then
|
|
echo "Timeout waiting for Playwright shared-session lock: $LOCK_DIR" >&2
|
|
exit 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
printf "%s\n" "$$" > "$LOCK_DIR/pid"
|
|
trap 'rm -rf "$LOCK_DIR" 2>/dev/null || true' EXIT INT TERM
|
|
}
|
|
|
|
run_pw() {
|
|
"$PWCLI" --session "$SESSION" "$@"
|
|
}
|
|
|
|
is_missing_session_error() {
|
|
local text="$1"
|
|
if echo "$text" | rg -qi "session.*not found|no browser|not open|closed"; then
|
|
return 0
|
|
fi
|
|
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
|
|
|
|
verb="${cmd[0]}"
|
|
|
|
if [[ "$verb" == "open" ]]; then
|
|
url=""
|
|
for (( i = 1; i < ${#cmd[@]}; i++ )); do
|
|
arg="${cmd[$i]}"
|
|
if [[ "$arg" != -* ]]; then
|
|
url="$arg"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ -n "$url" ]]; then
|
|
set +e
|
|
out="$(run_pw snapshot 2>&1)"
|
|
code=$?
|
|
set -e
|
|
if [[ $code -eq 0 ]]; then
|
|
run_pw goto "$url"
|
|
run_pw snapshot
|
|
exit 0
|
|
fi
|
|
if ! is_missing_session_error "$out"; then
|
|
echo "$out" >&2
|
|
exit $code
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
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
|
|
init_session
|
|
run_pw "${cmd[@]}"
|
|
exit 0
|
|
fi
|
|
|
|
echo "$out" >&2
|
|
exit $code
|