A small CLI that automates the PushPress Members web app to:
- Log in
- Extract workouts
- Book classes by day/time
This tool is built for the client/member experience at https://members.pushpress.com.
- Node.js
- Playwright browsers
Install dependencies:
npm installInstall Playwright browsers:
npx playwright install- Create a
.envfile (see Configuration below). - Install dependencies and Playwright browsers.
- Run
listto see available flows. - Run a flow, optionally with
--no-headless --pause --verbosefor debugging.
Example:
npx tsx src/cli.ts list
npx tsx src/cli.ts run login --no-headless --pause --verboseCreate a .env file in the project root (or pass --config <path>):
PUSHPRESS_EMAIL=you@example.com
PUSHPRESS_PASSWORD=your-password
PUSHPRESS_BASE_URL=https://members.pushpress.com
OPENAI_API_KEY=your-openai-keyOptional OpenAI settings (used for workout-week markdown summaries):
OPENAI_MODEL: defaultgpt-3.5-turbo-16kOPENAI_PROMPT_PATH: any file path (default:./prompts/workout-week-summary.md)
Example with a custom config file:
npx tsx src/cli.ts config --config ./envs/pushpress.envValidate the config before running a flow:
npx tsx src/cli.ts config --config ./envs/pushpress.env --validateList available flows:
npx tsx src/cli.ts listShow resolved config:
npx tsx src/cli.ts configValidate config:
npx tsx src/cli.ts config --validateValidate current session:
npx tsx src/cli.ts validate-session --verboseRun a flow:
npx tsx src/cli.ts run <flow-name>All known values:
--config <path>: any file path (default:.env)--verbose: boolean flag--headless/--no-headless: boolean flag (default:--headless)--slow-mo <ms>: integer milliseconds (e.g.,250)--timeout <ms>: integer milliseconds (e.g.,30000)--pause: boolean flag--dry-run: boolean flag (logs steps without executing)
Common option patterns:
# Debug a flow with a visible browser and slower actions
npx tsx src/cli.ts run workout-week --no-headless --slow-mo 250 --pause --verbose
# Use a non-default config file
npx tsx src/cli.ts run login --config ./envs/pushpress.env
# Increase timeouts for slow networks
npx tsx src/cli.ts run workout-history --timeout 60000 --verboseLogs into the Members app and saves a session to state/session.json.
npx tsx src/cli.ts run login --no-headless --verboseExtracts workout history (network capture) while on the Workouts tab.
npx tsx src/cli.ts run workout-history --no-headless --verboseOptional:
--workout-type <name>: selects a workout type from the dropdown (e.g.,Bootcamp,HYROX; quote multi-word names like"PRVN Pump").- When provided, output filenames include the workout type slug (e.g.,
workout-history-201530-bootcamp.json).
- When provided, output filenames include the workout type slug (e.g.,
Typical usage:
npx tsx src/cli.ts run workout-history --no-headless --verbose --pauseClicks each day in the week and captures workouts for all days. Output includes weekly arrays.
npx tsx src/cli.ts run workout-week --no-headless --verbose --pauseOptional:
--workout-type <name>: selects a workout type from the dropdown (e.g.,PRVN Burn; quote multi-word names like"PRVN Pump").- When provided, output filenames include the workout type slug (e.g.,
workout-week-201530-prvn-burn.json).
- When provided, output filenames include the workout type slug (e.g.,
This flow also writes a summary file grouped by day with only title, description, and workoutTitle, plus the detected date:
output/workout-week/YYYY-MM-DD/workout-week-HHmmss-summary.json
output/workout-week/YYYY-MM-DD/workout-week-HHmmss-<workout-type>-summary.json
If OPENAI_API_KEY is set, it also writes a formatted markdown summary using the prompt in ./prompts/workout-week-summary.md:
output/workout-week/YYYY-MM-DD/workout-week-HHmmss-summary.md
output/workout-week/YYYY-MM-DD/workout-week-HHmmss-<workout-type>-summary.md
Example: JSON only (no OpenAI key):
npx tsx src/cli.ts run workout-week --no-headless --verboseExample: with OpenAI markdown summary:
OPENAI_API_KEY=your-openai-key npx tsx src/cli.ts run workout-week --no-headless --verboseBooks classes for specific days and a time. This flow is dry-run by default.
Required:
--days <list>--time <label>
Optional: All known values:
--days <list>: comma/space separated day keys:sun, mon, tue, wed, thu, fri, sat--time <label>: time label as shown in UI, e.g.6:00 AM,5:00 PM--class <name>: class name label as shown in UI (default:CrossFit; quote multi-word names like"Olympic Lifting").--type <name>: alias for--class(quote multi-word names).--category <name>: one ofReservations,Classes,Appointments,Events(default:Classes)--week <which>:current,next, or an integer offset like2,3(max 6)--waitlist: allow waitlist-only booking--confirm: perform booking actions (without this it is dry-run)
Example dry-run:
npx tsx src/cli.ts run schedule-book --days mon,wed,fri --time "5:00 PM" --class "CrossFit" --no-headless --verbose --pauseExample confirm booking:
npx tsx src/cli.ts run schedule-book --days mon,wed,fri --time "5:00 PM" --class "CrossFit" --confirm --no-headless --verbose --pauseTarget next week:
npx tsx src/cli.ts run schedule-book --week next --days fri --time "5:00 PM" --class "CrossFit" --confirm --no-headless --verbose --pauseTarget next-next week:
npx tsx src/cli.ts run schedule-book --week 2 --days fri --time "5:00 PM" --class "CrossFit" --confirm --no-headless --verbose --pauseCommon booking variations:
# Book a different class name and allow waitlist
npx tsx src/cli.ts run schedule-book --days tue,thu --time "6:00 AM" --class "Olympic Lifting" --waitlist --confirm
# Book from the Reservations category
npx tsx src/cli.ts run schedule-book --category Reservations --days mon --time "12:00 PM" --confirm
# Dry-run for a future week offset
npx tsx src/cli.ts run schedule-book --week 3 --days fri --time "5:00 PM"All flows (except login) write JSON output to:
output/<flow-name>/<YYYY-MM-DD>/<flow-name-HHmmss>.json
For schedule-book, the output includes:
data.bookingsfor confirmed reservationsdata.matchesfor dry-run matchesdata.attemptsfor skipped or attempted bookings
- If login fails, delete
state/session.jsonand runloginagain. - If the UI is slow or flaky, use
--timeout 60000and--slow-mo 250. - If the flow seems stuck, rerun with
--no-headless --pause --verboseto inspect the browser state. - If OpenAI summaries fail, verify
OPENAI_API_KEYandOPENAI_PROMPT_PATH(if set).
- The CLI will reuse
state/session.jsonwhen available. If the session is invalid, it re-authenticates. - Use
--pauseto keep the browser open at the end of a run for debugging.