Pluggable CLI for booking restaurant reservations across Resy, OpenTable, Tock, and SevenRooms. Every provider is an independent module that plugs into the same interface; the CLI, OpenClaw plugin, and Claude Code plugin all read from the provider registry, not from any one provider.
Once published to npm:
npm i -g restaurant-cli
# or
npx restaurant-cli --helpFor local development before publish:
git clone https://github.com/omarshahine/restaurant-cli.git
cd restaurant-cli
npm install
npm run build
npm link # puts `restaurant` on your PATH
# OpenTable support (optional — browser automation peer deps):
npm i patchright
npx playwright install chromium# one-time credential setup per provider (email + password; token persisted)
restaurant setup resy
# sanity check — config, auth, scheduler health
restaurant doctor
# search
restaurant search "le bernardin"
restaurant search "carbone" --provider opentable
# availability + book (Resy)
restaurant availability --venue 1387 --date 2026-05-15 --party 2
restaurant book --venue 1387 --date 2026-05-15 --time 19:30 --party 2
# list + cancel
restaurant list --upcoming
restaurant cancel <reservation-id>
# snipe — queue a booking for when the reservation window opens
restaurant snipe --venue 1387 --date 2026-05-15 --time 19:30 --party 2 \
--release-at 2026-04-30T10:00-07:00
restaurant jobs list
restaurant jobs cancel <job-id>
restaurant jobs logs <job-id>
# OpenTable hand-off (no API booking; deep link → user confirms in browser)
restaurant book --venue 1046758 --date 2026-05-15 --time 19:00 --party 2 \
--provider opentableAll destructive commands (book, cancel, jobs cancel, snipe) prompt for y/N confirmation. Pass --yes to skip — useful for scripts and the snipe fire-time self-invocation.
| Command | Status |
|---|---|
setup <provider> |
✓ |
search <query> |
✓ |
doctor |
✓ |
version |
✓ |
availability |
✓ (Resy) |
book |
✓ (Resy) |
list |
✓ (Resy) |
cancel |
✓ (Resy) |
snipe |
✓ (Resy) |
jobs list/cancel/logs |
✓ |
config get/set/path |
✓ |
| Provider | search | availability | book | cancel | list | snipe | bookUrl |
|---|---|---|---|---|---|---|---|
| Resy | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — |
| OpenTable | ✓ | wired* | — | — | — | — | ✓ |
* OpenTable availability is coded against the /booking/restref/availability page's __NEXT_DATA__ hydration payload but capability stays false until the parser is live-verified against a real response. Flip capabilities.availability in src/providers/opentable/provider.ts once you've confirmed a successful scrape.
OpenTable has no public consumer API and Akamai Bot Manager blocks raw HTTP. Live venue search works via a browser-automation module: patchright (stealth-patched Playwright fork) + persistent Chrome profile + channel: "chrome" + ~4.5s mouse jitter defeats Akamai reliably. The module drives opentable.com's own homepage search and sniffs the Autocomplete GraphQL response. First run opens a headed Chrome window for ~5-10s — that's intentional; headless trips Akamai.
Booking completion is intentionally not available through the API — OpenTable confirmation requires a logged-in session + real user interaction, and automated confirmation has historically tripped bot-detection and accidentally completed real reservations (see mikehe123/opentable-reservations). The bookUrl capability generates a /restref/client hand-off URL (verified live 2026-04-18) that OpenTable redirects into its own booking flow with the time-slot picker rendered — you complete the reservation yourself.
Four consumers of the same core:
- Plain CLI —
restaurant <subcommand> - Library —
import { providers, Scheduler } from "restaurant-cli" - OpenClaw plugin — registers 6 provider-agnostic tools (
restaurant_search,restaurant_availability,restaurant_book,restaurant_schedule_snipe,restaurant_list,restaurant_cancel) via the host - Claude Code plugin — skill + router agent + provider-specific agents (
resy-agent,opentable-agent) + slash commands (/restaurant,/restaurant-setup,/restaurant-book,/restaurant-snipe,/restaurant-jobs), all shelling out to the CLI
The pluggable seam:
src/providers/
types.ts ← Provider interface + ProviderCapabilities
registry.ts ← runtime dispatcher
bootstrap.ts ← the ONLY file that knows every provider
resy/ ← first provider; future modules are peer directories
opentable/ ← second provider; proves the seam
# tock/, sevenrooms/ — added the same way
Adding a new provider is a two-file change: create src/providers/<name>/ implementing Provider, add one line to bootstrap.ts. No core-code changes.
Pre-npm-publish install from the repo:
git clone https://github.com/omarshahine/restaurant-cli.git
cd restaurant-cli
./scripts/install-openclaw.sh # deps + build + link bin + openclaw plugin register
restaurant setup resy-openclaw # auth + mirror creds into OpenClaw config
# Restart the OpenClaw gatewayThe -openclaw suffix on setup is the bridge: restaurant setup resy persists
credentials to ~/.secrets.env + ~/.config/restaurant-cli/config.yaml (CLI
store); appending -openclaw additionally mirrors them into
plugins.entries.restaurant-cli.config in ~/.openclaw/openclaw.json so the
gateway-side tools can find them. Works for any provider — opentable-openclaw,
tock-openclaw, etc.
Once the CLI is published to npm this collapses to:
npm i -g restaurant-cli
openclaw plugins install restaurant-cli
restaurant setup resy-openclawInstalls from Omar's private marketplace:
/plugin install restaurant-cli@omarshahine-plugins
See the skills/, agents/, and commands/ directories for the plugin surface.
~/.config/restaurant-cli/config.yaml— non-secret config (default provider, timezone, logging).~/.secrets.env— auth tokens (RESY_AUTH_TOKEN, etc.) referenced viaSecretRef.
Never uses macOS Keychain.
restaurant snipe queues a booking to fire at a specific release time via POSIX at:
--release-attakes ISO8601 with timezone offset (e.g.2026-04-30T10:00-07:00). Must be in the future.- The job is wrapped in a bash script that sources
~/.secrets.envat fire time so the auth token is present. Never written to the at-spool in plaintext. - Fire-time output goes to
~/.local/state/restaurant-cli/logs/<job-id>.log— JSONLsnipe.start/snipe.endevents plus therestaurant book --yes --jsonoutput. - Inspect via
restaurant jobs list/logs <id>; cancel viajobs cancel <id>(callsatrm+ removes the local metadata row).
Resolution is per-minute (POSIX at limit). Sub-minute sniping requires a daemon backend, which is not implemented.
The Resy provider module is a clean TypeScript reimplementation inspired by the design of lgrees/resy-cli (MIT). No code was copied; endpoint-level citations are inline in src/providers/resy/client.ts. See NOTICE.
MIT — see LICENSE.