Eurostar sells packages (a hotel stay bundled with a return train) through
the same staging /gateway site-api as ordinary train bookings. The bot books
them through a dedicated pipeline that mirrors the train search, then funnels
into the shared cart/checkout steps.
--package <destination> switches the run into packages mode. See
cli.md for the full flag list.
src/core/book.ts dispatches on --package: with it set, src/core/book/package.ts
(bookPackage) runs instead of src/core/book/train.ts (bookTrain). The
package pre-flight lives in src/core/web/packages/:
PackagesSearch — lists hotels for an origin/destination region, arrival
date, nights and room occupancy. The bot picks the hotel from --hotel
(name substring), else a known staging test hotel for the region, else the
cheapest result (the search is sorted price-ascending).PackageHotelRooms — lists bookable rooms for that hotel. --cancellation FULL|PARTIAL narrows to rooms offering that refund type (each room carries a
cancellations[] policy list); --room narrows by name/type substring.PackageTrains — lists outbound/inbound trains and the gateway's
pre-selected service per bound (same journey/fare shape as journeySearch).
By default the bot books the pre-selected trains; any --out/--rtn train
flag re-runs the standard journey selector over the package journeys.createCart (package variant) — channel: "PKG", the package total in
package: { totalPrice }, and the hotel/room detail in
hotels: [CartItemHotelInput]. Trains are ordinary TrainInput, identical to
the train path. Then holdCartProducts and checkout run unchanged.The search calls send the x-channel: pack HTTP header; createCart does not
(its GraphQL channel: "PKG" selects the packages flow). __typename is
stripped from response-derived errata/cancellations before they go back into
the createCart input types (which reject it).
Packages address cities by region id, not station UIC (london = 279,
antwerp = 2385, paris = 390). The table in
src/core/lib/booking/packages/regions.ts is mirrored from the hotel-inventory
service (src/graphql/resolvers/regions/data/regions.ts and
regionConnections.ts) and drives --package/--from slug resolution and
route validation (assertPackageRoute). Not every origin reaches every
destination; an invalid pair errors with the list of valid destinations.
variables/packageTestHotels.csv (human reference) and
variables/packageTestHotels.json (consumed by the selector) list the known
staging test hotels per city. PRID is not reliably the live staging hotel id
BASEL and LAUSANNE in the list have no package region and are
reference-only.bun book --package antwerp --from london -d 2026-07-10 --nights 2 -p 2a \
--hotel hampton --room triple --cancellation FULL -e stg -v
Everything is staging-only with a test card - no real bookings.