{"id":"bde05c5d-c797-408b-b186-5ab6a0baa4c1","entityType":"agent","slug":"clawhub-chrischall-easytable-fpx","name":"easytable-fpx","canonicalUrl":"https://www.xpersona.co/agent/clawhub-chrischall-easytable-fpx","canonicalPath":"/agent/clawhub-chrischall-easytable-fpx","generatedAt":"2026-10-10T14:46:13.737Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T11:55:10.704Z","emptyReason":null},"description":"Check restaurant availability and manage easyTable bookings (book.easytable.com/book/?id=<placeId>) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the easytable-mcp server — list booking areas/dates/times, look up a booking by phone, and cancel it, all one-shot through a signed-in browser tab. Use when you want easyTable access without the MCP, in a script, or on a machine where the MCP isn't installed.","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.4K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:easytable-fpx","sourceUrl":"https://clawhub.ai/chrischall/easytable-fpx","homepage":"https://clawhub.ai/chrischall/skills/easytable-fpx","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/chrischall/easytable-fpx","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/chrischall/skills/easytable-fpx","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":63,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"easytable-fpx technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T11:55:10.704Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T11:55:10.704Z","emptyReason":null},"stars":null,"forks":null,"downloads":1449,"packageName":null,"latestVersion":"1.2.6","tractionLabel":"1.4K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T11:55:10.635Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T11:55:10.704Z","lastCrawledAt":"2026-10-10T11:55:10.635Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T11:55:10.635Z","lastVerifiedAt":null,"highlights":[{"version":"1.2.6","createdAt":"2026-10-09T23:24:32.307Z","changelog":"easytable-fpx 1.2.6 - Removed the sample skill-card.md file for a leaner package. - No changes to functionality or documented usage. - Documentation in SKILL.md remains unchanged.","fileCount":4,"zipByteSize":6596},{"version":"1.2.5","createdAt":"2026-10-07T13:36:48.299Z","changelog":"- Removed the file skill-card.md. - No changes to functionality or documentation content.","fileCount":4,"zipByteSize":6692},{"version":"1.2.4","createdAt":"2026-10-05T02:49:23.231Z","changelog":"- Removed the file skill-card.md. - No changes to functionality or usage. - Documentation and usage instructions remain the same.","fileCount":4,"zipByteSize":6570},{"version":"1.2.3","createdAt":"2026-10-03T01:39:46.892Z","changelog":"easytable-fpx 1.2.3 - Removed the file skill-card.md. - No changes to functional documentation or code behavior.","fileCount":4,"zipByteSize":6715},{"version":"1.2.2","createdAt":"2026-10-01T18:24:29.859Z","changelog":"- Removed the sample skill-card.md file. - No functional changes; documentation and core usage remain the same.","fileCount":4,"zipByteSize":6672},{"version":"1.2.1","createdAt":"2026-09-28T13:57:38.828Z","changelog":"- Updated all instructions and references to use the new ContextMint Bridge browser extension (replacing the previous \"Transporter\" extension). - Added installation and verification details for the ContextMint Bridge extension, including platform-specific notes for Chrome and Safari. - Removed outdated documentation file (skill-card.md).","fileCount":4,"zipByteSize":6712},{"version":"1.2.0","createdAt":"2026-09-24T15:12:22.474Z","changelog":"easytable-fpx 1.2.0 - Minor documentation update: clarified that confirmed write operations will book or cancel a real restaurant table, updating prior \"confirm-equivalent action\" wording. - Removed the skill-card.md file. No functional changes to code or interface.","fileCount":4,"zipByteSize":6587},{"version":"1.1.3","createdAt":"2026-09-23T21:40:06.693Z","changelog":"- Removed the sample file skill-card.md. - No functional or behavioral changes to the skill itself.","fileCount":4,"zipByteSize":6588}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:easytable-fpx","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:easytable-fpx` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/chrischall/easytable-fpx before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-easytable-fpx/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-easytable-fpx/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-easytable-fpx/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-easytable-fpx/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-easytable-fpx/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-easytable-fpx/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T14:46:13.734Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-easytable-fpx/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-easytable-fpx/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-easytable-fpx/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-easytable-fpx/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T11:55:10.704Z","emptyReason":null},"readme":"Skill: easytable-fpx\n\nOwner: chrischall\n\nSummary: Check restaurant availability and manage easyTable bookings (book.easytable.com/book/?id=<placeId>) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the easytable-mcp server — list booking areas/dates/times, look up a booking by phone, and cancel it, all one-shot through a signed-in browser tab. Use when you want easyTable access without the MCP, in a script, or on a machine where the MCP isn't installed.\n\nTags: latest:1.2.6\n\nVersion history:\n\nv1.2.6 | 2026-10-09T23:24:32.307Z | auto\n\neasytable-fpx 1.2.6\n\n- Removed the sample skill-card.md file for a leaner package.\n- No changes to functionality or documented usage.\n- Documentation in SKILL.md remains unchanged.\n\nv1.2.5 | 2026-10-07T13:36:48.299Z | auto\n\n- Removed the file skill-card.md.\n- No changes to functionality or documentation content.\n\nv1.2.4 | 2026-10-05T02:49:23.231Z | auto\n\n- Removed the file skill-card.md.\n- No changes to functionality or usage.\n- Documentation and usage instructions remain the same.\n\nv1.2.3 | 2026-10-03T01:39:46.892Z | auto\n\neasytable-fpx 1.2.3\n\n- Removed the file skill-card.md.\n- No changes to functional documentation or code behavior.\n\nv1.2.2 | 2026-10-01T18:24:29.859Z | auto\n\n- Removed the sample skill-card.md file.\n- No functional changes; documentation and core usage remain the same.\n\nv1.2.1 | 2026-09-28T13:57:38.828Z | auto\n\n- Updated all instructions and references to use the new ContextMint Bridge browser extension (replacing the previous \"Transporter\" extension).\n- Added installation and verification details for the ContextMint Bridge extension, including platform-specific notes for Chrome and Safari.\n- Removed outdated documentation file (skill-card.md).\n\nv1.2.0 | 2026-09-24T15:12:22.474Z | auto\n\neasytable-fpx 1.2.0\n\n- Minor documentation update: clarified that confirmed write operations will book or cancel a real restaurant table, updating prior \"confirm-equivalent action\" wording.\n- Removed the skill-card.md file. No functional changes to code or interface.\n\nv1.1.3 | 2026-09-23T21:40:06.693Z | auto\n\n- Removed the sample file skill-card.md.\n- No functional or behavioral changes to the skill itself.\n\nv1.1.2 | 2026-09-23T16:01:39.808Z | auto\n\n- Removed the sample skill card file (skill-card.md).\n- No user-facing changes to core features or documentation.\n- Housekeeping update: cleans up redundant project files.\n\nv1.1.1 | 2026-09-21T04:14:37.459Z | auto\n\n- Removed the sample skill-card.md file.\n- No changes to functionality or documented behavior.\n\nv1.1.0 | 2026-09-20T02:50:26.020Z | auto\n\n- Removed the sample file skill-card.md.\n- No functional or documentation changes to the skill itself.\n- Minor update streamlines the project by deleting unnecessary sample content.\n\nv1.0.0 | 2026-09-17T23:35:45.536Z | auto\n\neasytable-fpx 1.0.0\n\n- Initial release.\n- Manage easyTable bookings (availability, lookup, cancel) via the fpx CLI and a signed-in browser tab—no MCP server required.\n- Removed sample file: skill-card.md.\n\nv0.4.4 | 2026-09-15T19:24:26.174Z | auto\n\n- Removed the sample file skill-card.md.\n- No user-facing or functionality changes; internal file cleanup only.\n\nv0.4.3 | 2026-09-14T14:11:06.803Z | auto\n\n- Removed the file: skill-card.md.\n- No feature or documentation changes in this version.\n- Housekeeping update with no visible impact on functionality or usage.\n\nv0.4.2 | 2026-09-10T17:53:59.739Z | auto\n\n- Removed the sample file skill-card.md.\n- No other changes to functionality or documentation.\n\nv0.4.1 | 2026-09-09T21:19:25.825Z | auto\n\n- Removed the sample file skill-card.md.\n- No changes to skill functionality or documentation.\n\nv0.4.0 | 2026-09-04T22:23:01.058Z | auto\n\n- Removed the file: skill-card.md.\n- No changes to the documented usage or behavior in SKILL.md.\n- Version bump to 0.4.0 to reflect housekeeping update.\n\nv0.2.3 | 2026-08-06T00:42:41.237Z | auto\n\n- Removed the file: skill-card.md\n- No changes to SKILL.md content or core functionality\n- Maintenance update to clean up unneeded documentation files\n\nv0.2.2 | 2026-07-30T12:53:50.447Z | auto\n\n- Removed the sample file skill-card.md.\n- No changes to functionality or documentation content.\n\nv0.2.1 | 2026-07-27T02:56:14.023Z | auto\n\neasytable-fpx v0.2.1\n\n- Provides a command-line tool to check restaurant availability and manage easyTable bookings using the fpx CLI—no easytable-mcp server required.\n- Routes requests via a signed-in browser tab with the Transporter extension to bypass Cloudflare protections on book.easytable.com.\n- Lists booking areas, available dates/times, retrieves bookings by phone, and cancels bookings—each as a single CLI command.\n- Includes setup steps for pairing the CLI with your browser, and usage recipes for all main actions.\n- Creating or modifying bookings via fpx requires a manual workaround to supply the Turnstile token due to CLI limitations.\n- Clarifies expected exit codes and troubleshooting for connectivity or botwall issues.\n\nArchive index:\n\nArchive v1.2.6: 4 files, 6596 bytes\n\nFiles: references/easytable-requests.md (5701b), skill-card.md (2005b), SKILL.md (5044b), _meta.json (132b)\n\nFile v1.2.6:SKILL.md\n\n---\nname: easytable-fpx\ndescription: >-\n  Check restaurant availability and manage easyTable bookings\n  (book.easytable.com/book/?id=<placeId>) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the easytable-mcp server — list\n  booking areas/dates/times, look up a booking by phone, and cancel it, all\n  one-shot through a signed-in browser tab. Use when you want easyTable\n  access without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# easyTable via fpx (no MCP)\n\neasyTable fronts every `book.easytable.com` endpoint — reads and writes alike\n— with a Cloudflare check that 403s any plain `curl`/Node request. `fpx`\nroutes the request through the user's own signed-in browser tab (the\nContextMint Bridge extension), which has already cleared the challenge, so the same\nrequests succeed. There's no easyTable login: a restaurant is identified only\nby its `id` (a.k.a. `place`) from its widget URL\n`https://book.easytable.com/book/?id=<placeId>`.\n\nThis is the same data/actions the `easytable_*` MCP tools use, reached with\none-shot CLI calls instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli               # provides `fpx`\nfpx profile add easytable --domain easytable.com\nfpx pair -p easytable                        # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** extension installed (from\nhttps://github.com/nullnet-app/contextmint-bridge/releases — Chrome: load the\nchrome zip unpacked; Safari: ships inside the ContextMint app, no public download\nyet, so use Chrome for now — it is the renamed fetchproxy extension, same maintainer; source is public, so\nbuild it or verify the zip with its published `.sha256`), with an open\n`https://book.easytable.com/book/?id=<placeId>` tab left to finish loading\n(its Turnstile check solves itself), and Chrome **Site access** allowing\n`easytable.com`. Pairing persists after the first approval.\n\n## Core call\n\nReads are GET requests that return **HTML fragments** (not JSON) — the\nwidget loads them into a container div. Send the `X-Requested-With` header\nthe widget sends, and pipe the fragment to `grep`/`sed` rather than `jq`:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest'\n```\n\nReady-to-run URLs + extraction recipes for every read, plus the cancel write\nand the create/modify payload shape, are in\n`references/easytable-requests.md`. Exhaustive parsing lives in the repo at\n`src/parse.ts` / `docs/EASYTABLE-API.md` — the recipes here are compact\ngreps against the same markup those parsers target.\n\n## The one rule: resolve the `type` first\n\nDates and times are scoped to a booking area. Always list types first, take\nthe `type` id, then feed it to dates/times:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nSame pattern for cancelling: resolve the booking id via `cancel-search.asp`\n(by mobile number) before calling the cancel endpoint.\n\n## Creating or modifying a booking — not fully doable through fpx\n\n`easytable_create_booking`/`easytable_modify_booking` need a live Cloudflare\n**Turnstile token**, read from the widget's hidden\n`input[name=\"cf-turnstile-response\"]`. The MCP harvests it with a bespoke\n`read_dom` verb wired into *this repo's own* fetchproxy transport\n(`bridge-fetchproxy.ts`) — the stock `fpx` CLI has no DOM-read command (only\n`get`/`post-json`/`request`/`cookies`/`local-storage`/`session-storage`/\n`indexeddb`/`session`), so it cannot harvest that token.\n\nThe other three page-derived values (`bookingToken`, `cancellationtime`,\n`lcid`) ARE plain server-rendered text, so `fpx get` + `grep` gets them fine\n— see `references/easytable-requests.md` for the full payload shape and a\nmanual-token workaround (read the Turnstile input yourself from the browser\nconsole). Treat create/modify as a manual/hybrid flow, not a clean one-shot.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. Write endpoints answer HTTP 200 even on a *rejected*\n  booking (`Status:0` in the body) — always check the parsed `Status`, not\n  just the exit code.\n- `2` — bridge unavailable: extension not connected or pairing pending → run\n  `fpx pair -p easytable`, confirm an easytable tab is open.\n- `3` — bot wall: the tab hasn't cleared Cloudflare → open/refresh a\n  `book.easytable.com` tab and retry.\n- `4` — upstream non-2xx from easyTable.\n\n## Notes\n\n- No easyTable account or API key involved — this is the user's own action\n  on their own signed-in browser session.\n- `fpx health -p easytable` shows bridge connection state when a call fails.\n- Don't fire real create/cancel calls just to \"test\" the flow — a\n  confirmed write here books/cancels a real table at a real\n  restaurant.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.2.6:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"easytable-fpx\",\n  \"version\": \"1.2.6\",\n  \"publishedAt\": 1791588272307\n}\n\nFile v1.2.6:references/easytable-requests.md\n\n# easyTable requests for fpx\n\nReady-to-run URLs for `fpx get '<url>' -p easytable -H 'X-Requested-With: XMLHttpRequest'`\n(reads) and `fpx post-json`/`fpx get` (writes). All paths/params/field shapes\nare transcribed from `src/client.ts`, `src/parse.ts`, `src/payload.ts`, and\n`docs/EASYTABLE-API.md` — the same shapes the `easytable_*` MCP tools use.\n\nHost: `https://book.easytable.com`. Every URL below needs `id=<placeId>`\n(a.k.a. `place`) and `lang=<code>` (`en`, `se`, `da`, … — defaults to `en`).\nReplace `1fdfc` with a real restaurant id from a\n`book.easytable.com/book/?id=<id>` link.\n\nReads answer **HTML fragments**, not JSON — extraction below is `grep -oE`\nagainst the exact attributes `src/parse.ts` selects on. These are compact\ngreps, not a full parser; for anything beyond simple extraction, read\n`src/parse.ts`.\n\n---\n\n## 1. List booking areas/types (resolve this first)\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nThe numeric value after `type=` in the `href` is the `type` id every later\ncall needs.\n\n## 2. List bookable dates\n\nNeeds `type` (from §1) and `qty` (party size).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/calendar.asp?id=1fdfc&lang=en&type=13991&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*day[^\"]*\"[^>]*data-date=\"[0-9-]+\"[^>]*>'\n```\n\nA `class` containing `av` = available; `closed`/`ua` = not. `data-note`\n(when present) carries a note.\n\n## 3. List time slots\n\nNeeds `type`, a `date` (ISO `YYYY-MM-DD`, from §2), and `qty`. Slot values\nare **minute-of-day** (1035 → 17:15 — `hh=floor(m/60)`, `mm=m%60`).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/times.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*time[^\"]*\"[^>]*data-time=\"[0-9]+\"[^>]*>'\n```\n\n## 4. Find existing bookings by phone\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/cancel-search.asp?id=1fdfc&lang=en&mobile=%2B46701234567' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE 'data-booking=\"[^\"]+\"|data-id=\"[^\"]+\"|data-mobile=\"[^\"]+\"'\n```\n\nURL-encode the mobile (`+` → `%2B`). The booking id is on\n`data-booking`/`data-id` (`easytable-mcp` prefers `data-booking`, falling\nback to `data-id` — never `value`, which can be a button label).\n\n## 5. Cancel a booking (no Turnstile — plain tokenless GET)\n\n```sh\nfpx get 'https://book.easytable.com/user/ajax/json_cancel_booking.asp?place=1fdfc&mobile=%2B46701234567&booking=<bookingId>' \\\n  -p easytable\n```\n\nThe response is JSONP-wrapped (`callback([{Status,...}])`); unwrap and read\nwith:\n\n```sh\nsed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] | {Status, errHtml, successHtml, confirmUrl}'\n```\n\n`Status: 1` = cancelled; `Status: 0` = rejected (see `errHtml`).\n\n## 6. Create / modify a booking — payload shape (Turnstile caveat applies)\n\nHarvest the three plain-text page-derived values first — these ARE\nreachable via `fpx get` (they're server-rendered, unlike the Turnstile\ntoken):\n\n```sh\n# bookingToken + cancellationtime, from the confirm.asp fragment:\nfpx get 'https://book.easytable.com/book/ajax/confirm.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&time=1035&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' > /tmp/confirm.html\ngrep -oE 'bookingToken\\s*=\\s*\"[^\"]+\"' /tmp/confirm.html          # → {EB9351E5-...}\ngrep -oE 'id=\"cancellationtime\"[^>]*value=\"[0-9]+\"' /tmp/confirm.html  # → e.g. 180\n\n# lcid, from the widget page HTML (language-dependent: se→1053, en→2057):\nfpx get 'https://book.easytable.com/book/?id=1fdfc&lang=en' -p easytable \\\n  | grep -oE '\\blcid\\s*=\\s*\"?[0-9]+'\n```\n\nThe **Turnstile token cannot be harvested through `fpx`** — the CLI has no\nDOM-read verb. Manual workaround: with the widget tab open and loaded (past\nthe confirm step so Turnstile has rendered), open DevTools console on that\ntab and run:\n\n```js\ndocument.querySelector('input[name=\"cf-turnstile-response\"]').value\n```\n\nCopy that value in as `turnstileToken` below. It's single-use and expires in\n~300s, so harvest it right before the POST.\n\nPOST body — the full 32-key `dataObj` (the endpoint 500s on a partial\npayload; `time`/`persons` MUST be strings; omit `event`/`existing` when\nunused):\n\n```json\n{\n  \"place\": \"1fdfc\",\n  \"type\": \"13991\",\n  \"date\": \"2026-08-01\",\n  \"persons\": \"2\",\n  \"time\": \"1035\",\n  \"name\": \"Guest Name\",\n  \"mobile\": \"+46701234567\",\n  \"email\": \"\",\n  \"room\": \"\",\n  \"company\": \"\",\n  \"address\": \"\",\n  \"zip\": \"\",\n  \"city\": \"\",\n  \"country\": \"\",\n  \"comment\": \"\",\n  \"newsletter\": 0,\n  \"newsletterrelated\": 0,\n  \"lcid\": \"2057\",\n  \"lang\": \"en\",\n  \"ref\": \"\",\n  \"cancellationtime\": \"180\",\n  \"promocode\": \"\",\n  \"groupRequestTerms\": 0,\n  \"preorder\": {},\n  \"tags\": [],\n  \"amounttags\": [],\n  \"customFields\": [],\n  \"bookingInfo\": [],\n  \"bookingToken\": \"{EB9351E5-...}\",\n  \"botScore\": 0,\n  \"websitePot\": \"\",\n  \"turnstileToken\": \"<paste the harvested token>\"\n}\n```\n\n```sh\nfpx post-json 'https://book.easytable.com/user/ajax/json_booking.asp' @/tmp/booking.json -p easytable \\\n  | sed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] // .'\n```\n\nFor **modify**, POST the same shape plus `\"existing\": \"<existingBookingId>\"`\nto `https://book.easytable.com/user/ajax/json_modify_booking.asp`.\n\nA `Status: 0` response with the token/bookingToken/cancellationtime fields\nall present and non-empty means the request itself is malformed — re-check\nthe harvested values and the field types (`time`/`persons` as strings).\n\nFile v1.2.6:skill-card.md\n\n## Description:\n\nGuides agents through checking easyTable restaurant availability and managing reservations with shell commands routed through a paired browser tab.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents can check restaurant booking areas, dates, and times, find reservations by phone, and cancel authorized bookings from a shell. Creating or modifying a booking requires a manual browser step.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Browser-session access to protected easyTable endpoints may expose reservation information.\n\nMitigation: Review the external CLI and browser extension before pairing, and use only a browser session you are authorized to operate.\n\nRisk: Cancellation, creation, or modification can change a real restaurant booking.\n\nMitigation: Confirm authorization and booking details before any write; do not send live writes merely to test the flow.\n\n## Reference(s):\n\n- [easyTable request recipes](references/easytable-requests.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [easytable-fpx release](https://clawhub.ai/chrischall/skills/easytable-fpx)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance, Configuration instructions]\n\n**Output Format:** [Markdown with shell commands and example booking payloads]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Create and modify flows require a manual browser step; booking writes can affect real reservations.]\n\n## Skill Version(s):\n\n1.2.6 (source: server-resolved release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.5: 4 files, 6692 bytes\n\nFiles: references/easytable-requests.md (5701b), skill-card.md (2187b), SKILL.md (5044b), _meta.json (132b)\n\nFile v1.2.5:SKILL.md\n\n---\nname: easytable-fpx\ndescription: >-\n  Check restaurant availability and manage easyTable bookings\n  (book.easytable.com/book/?id=<placeId>) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the easytable-mcp server — list\n  booking areas/dates/times, look up a booking by phone, and cancel it, all\n  one-shot through a signed-in browser tab. Use when you want easyTable\n  access without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# easyTable via fpx (no MCP)\n\neasyTable fronts every `book.easytable.com` endpoint — reads and writes alike\n— with a Cloudflare check that 403s any plain `curl`/Node request. `fpx`\nroutes the request through the user's own signed-in browser tab (the\nContextMint Bridge extension), which has already cleared the challenge, so the same\nrequests succeed. There's no easyTable login: a restaurant is identified only\nby its `id` (a.k.a. `place`) from its widget URL\n`https://book.easytable.com/book/?id=<placeId>`.\n\nThis is the same data/actions the `easytable_*` MCP tools use, reached with\none-shot CLI calls instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli               # provides `fpx`\nfpx profile add easytable --domain easytable.com\nfpx pair -p easytable                        # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** extension installed (from\nhttps://github.com/nullnet-app/contextmint-bridge/releases — Chrome: load the\nchrome zip unpacked; Safari: ships inside the ContextMint app, no public download\nyet, so use Chrome for now — it is the renamed fetchproxy extension, same maintainer; source is public, so\nbuild it or verify the zip with its published `.sha256`), with an open\n`https://book.easytable.com/book/?id=<placeId>` tab left to finish loading\n(its Turnstile check solves itself), and Chrome **Site access** allowing\n`easytable.com`. Pairing persists after the first approval.\n\n## Core call\n\nReads are GET requests that return **HTML fragments** (not JSON) — the\nwidget loads them into a container div. Send the `X-Requested-With` header\nthe widget sends, and pipe the fragment to `grep`/`sed` rather than `jq`:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest'\n```\n\nReady-to-run URLs + extraction recipes for every read, plus the cancel write\nand the create/modify payload shape, are in\n`references/easytable-requests.md`. Exhaustive parsing lives in the repo at\n`src/parse.ts` / `docs/EASYTABLE-API.md` — the recipes here are compact\ngreps against the same markup those parsers target.\n\n## The one rule: resolve the `type` first\n\nDates and times are scoped to a booking area. Always list types first, take\nthe `type` id, then feed it to dates/times:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nSame pattern for cancelling: resolve the booking id via `cancel-search.asp`\n(by mobile number) before calling the cancel endpoint.\n\n## Creating or modifying a booking — not fully doable through fpx\n\n`easytable_create_booking`/`easytable_modify_booking` need a live Cloudflare\n**Turnstile token**, read from the widget's hidden\n`input[name=\"cf-turnstile-response\"]`. The MCP harvests it with a bespoke\n`read_dom` verb wired into *this repo's own* fetchproxy transport\n(`bridge-fetchproxy.ts`) — the stock `fpx` CLI has no DOM-read command (only\n`get`/`post-json`/`request`/`cookies`/`local-storage`/`session-storage`/\n`indexeddb`/`session`), so it cannot harvest that token.\n\nThe other three page-derived values (`bookingToken`, `cancellationtime`,\n`lcid`) ARE plain server-rendered text, so `fpx get` + `grep` gets them fine\n— see `references/easytable-requests.md` for the full payload shape and a\nmanual-token workaround (read the Turnstile input yourself from the browser\nconsole). Treat create/modify as a manual/hybrid flow, not a clean one-shot.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. Write endpoints answer HTTP 200 even on a *rejected*\n  booking (`Status:0` in the body) — always check the parsed `Status`, not\n  just the exit code.\n- `2` — bridge unavailable: extension not connected or pairing pending → run\n  `fpx pair -p easytable`, confirm an easytable tab is open.\n- `3` — bot wall: the tab hasn't cleared Cloudflare → open/refresh a\n  `book.easytable.com` tab and retry.\n- `4` — upstream non-2xx from easyTable.\n\n## Notes\n\n- No easyTable account or API key involved — this is the user's own action\n  on their own signed-in browser session.\n- `fpx health -p easytable` shows bridge connection state when a call fails.\n- Don't fire real create/cancel calls just to \"test\" the flow — a\n  confirmed write here books/cancels a real table at a real\n  restaurant.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.2.5:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"easytable-fpx\",\n  \"version\": \"1.2.5\",\n  \"publishedAt\": 1791380208299\n}\n\nFile v1.2.5:references/easytable-requests.md\n\n# easyTable requests for fpx\n\nReady-to-run URLs for `fpx get '<url>' -p easytable -H 'X-Requested-With: XMLHttpRequest'`\n(reads) and `fpx post-json`/`fpx get` (writes). All paths/params/field shapes\nare transcribed from `src/client.ts`, `src/parse.ts`, `src/payload.ts`, and\n`docs/EASYTABLE-API.md` — the same shapes the `easytable_*` MCP tools use.\n\nHost: `https://book.easytable.com`. Every URL below needs `id=<placeId>`\n(a.k.a. `place`) and `lang=<code>` (`en`, `se`, `da`, … — defaults to `en`).\nReplace `1fdfc` with a real restaurant id from a\n`book.easytable.com/book/?id=<id>` link.\n\nReads answer **HTML fragments**, not JSON — extraction below is `grep -oE`\nagainst the exact attributes `src/parse.ts` selects on. These are compact\ngreps, not a full parser; for anything beyond simple extraction, read\n`src/parse.ts`.\n\n---\n\n## 1. List booking areas/types (resolve this first)\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nThe numeric value after `type=` in the `href` is the `type` id every later\ncall needs.\n\n## 2. List bookable dates\n\nNeeds `type` (from §1) and `qty` (party size).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/calendar.asp?id=1fdfc&lang=en&type=13991&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*day[^\"]*\"[^>]*data-date=\"[0-9-]+\"[^>]*>'\n```\n\nA `class` containing `av` = available; `closed`/`ua` = not. `data-note`\n(when present) carries a note.\n\n## 3. List time slots\n\nNeeds `type`, a `date` (ISO `YYYY-MM-DD`, from §2), and `qty`. Slot values\nare **minute-of-day** (1035 → 17:15 — `hh=floor(m/60)`, `mm=m%60`).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/times.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*time[^\"]*\"[^>]*data-time=\"[0-9]+\"[^>]*>'\n```\n\n## 4. Find existing bookings by phone\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/cancel-search.asp?id=1fdfc&lang=en&mobile=%2B46701234567' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE 'data-booking=\"[^\"]+\"|data-id=\"[^\"]+\"|data-mobile=\"[^\"]+\"'\n```\n\nURL-encode the mobile (`+` → `%2B`). The booking id is on\n`data-booking`/`data-id` (`easytable-mcp` prefers `data-booking`, falling\nback to `data-id` — never `value`, which can be a button label).\n\n## 5. Cancel a booking (no Turnstile — plain tokenless GET)\n\n```sh\nfpx get 'https://book.easytable.com/user/ajax/json_cancel_booking.asp?place=1fdfc&mobile=%2B46701234567&booking=<bookingId>' \\\n  -p easytable\n```\n\nThe response is JSONP-wrapped (`callback([{Status,...}])`); unwrap and read\nwith:\n\n```sh\nsed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] | {Status, errHtml, successHtml, confirmUrl}'\n```\n\n`Status: 1` = cancelled; `Status: 0` = rejected (see `errHtml`).\n\n## 6. Create / modify a booking — payload shape (Turnstile caveat applies)\n\nHarvest the three plain-text page-derived values first — these ARE\nreachable via `fpx get` (they're server-rendered, unlike the Turnstile\ntoken):\n\n```sh\n# bookingToken + cancellationtime, from the confirm.asp fragment:\nfpx get 'https://book.easytable.com/book/ajax/confirm.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&time=1035&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' > /tmp/confirm.html\ngrep -oE 'bookingToken\\s*=\\s*\"[^\"]+\"' /tmp/confirm.html          # → {EB9351E5-...}\ngrep -oE 'id=\"cancellationtime\"[^>]*value=\"[0-9]+\"' /tmp/confirm.html  # → e.g. 180\n\n# lcid, from the widget page HTML (language-dependent: se→1053, en→2057):\nfpx get 'https://book.easytable.com/book/?id=1fdfc&lang=en' -p easytable \\\n  | grep -oE '\\blcid\\s*=\\s*\"?[0-9]+'\n```\n\nThe **Turnstile token cannot be harvested through `fpx`** — the CLI has no\nDOM-read verb. Manual workaround: with the widget tab open and loaded (past\nthe confirm step so Turnstile has rendered), open DevTools console on that\ntab and run:\n\n```js\ndocument.querySelector('input[name=\"cf-turnstile-response\"]').value\n```\n\nCopy that value in as `turnstileToken` below. It's single-use and expires in\n~300s, so harvest it right before the POST.\n\nPOST body — the full 32-key `dataObj` (the endpoint 500s on a partial\npayload; `time`/`persons` MUST be strings; omit `event`/`existing` when\nunused):\n\n```json\n{\n  \"place\": \"1fdfc\",\n  \"type\": \"13991\",\n  \"date\": \"2026-08-01\",\n  \"persons\": \"2\",\n  \"time\": \"1035\",\n  \"name\": \"Guest Name\",\n  \"mobile\": \"+46701234567\",\n  \"email\": \"\",\n  \"room\": \"\",\n  \"company\": \"\",\n  \"address\": \"\",\n  \"zip\": \"\",\n  \"city\": \"\",\n  \"country\": \"\",\n  \"comment\": \"\",\n  \"newsletter\": 0,\n  \"newsletterrelated\": 0,\n  \"lcid\": \"2057\",\n  \"lang\": \"en\",\n  \"ref\": \"\",\n  \"cancellationtime\": \"180\",\n  \"promocode\": \"\",\n  \"groupRequestTerms\": 0,\n  \"preorder\": {},\n  \"tags\": [],\n  \"amounttags\": [],\n  \"customFields\": [],\n  \"bookingInfo\": [],\n  \"bookingToken\": \"{EB9351E5-...}\",\n  \"botScore\": 0,\n  \"websitePot\": \"\",\n  \"turnstileToken\": \"<paste the harvested token>\"\n}\n```\n\n```sh\nfpx post-json 'https://book.easytable.com/user/ajax/json_booking.asp' @/tmp/booking.json -p easytable \\\n  | sed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] // .'\n```\n\nFor **modify**, POST the same shape plus `\"existing\": \"<existingBookingId>\"`\nto `https://book.easytable.com/user/ajax/json_modify_booking.asp`.\n\nA `Status: 0` response with the token/bookingToken/cancellationtime fields\nall present and non-empty means the request itself is malformed — re-check\nthe harvested values and the field types (`time`/`persons` as strings).\n\nFile v1.2.5:skill-card.md\n\n## Description:\n\nGuides agents in checking easyTable restaurant availability, finding bookings by phone, and managing bookings through a paired browser session with the fpx CLI.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and other agents using easyTable can check restaurant booking areas, dates, and times, locate existing bookings by phone number, and cancel reservations from a shell without an MCP server. Creating or modifying a booking requires a manual browser step.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The paired browser extension and fpx can access the user's easyTable browser context.\n\nMitigation: Install and pair only if comfortable granting that access, and limit its use to the intended easyTable session.\n\nRisk: Phone numbers, booking IDs, and Turnstile tokens are sensitive booking data.\n\nMitigation: Keep these values private and avoid exposing them in logs or shared output.\n\nRisk: Create, modify, and cancel requests can change real restaurant reservations.\n\nMitigation: Obtain explicit user confirmation before each write request; do not test by making live booking changes.\n\n## Reference(s):\n\n- [easyTable requests for fpx](artifact/references/easytable-requests.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [easytable-fpx on ClawHub](https://clawhub.ai/chrischall/skills/easytable-fpx)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell commands and booking request examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Read requests return HTML fragments; booking changes require checking the response status.]\n\n## Skill Version(s):\n\n1.2.5 (source: ClawHub release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.4: 4 files, 6570 bytes\n\nFiles: references/easytable-requests.md (5701b), skill-card.md (1998b), SKILL.md (5044b), _meta.json (132b)\n\nFile v1.2.4:SKILL.md\n\n---\nname: easytable-fpx\ndescription: >-\n  Check restaurant availability and manage easyTable bookings\n  (book.easytable.com/book/?id=<placeId>) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the easytable-mcp server — list\n  booking areas/dates/times, look up a booking by phone, and cancel it, all\n  one-shot through a signed-in browser tab. Use when you want easyTable\n  access without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# easyTable via fpx (no MCP)\n\neasyTable fronts every `book.easytable.com` endpoint — reads and writes alike\n— with a Cloudflare check that 403s any plain `curl`/Node request. `fpx`\nroutes the request through the user's own signed-in browser tab (the\nContextMint Bridge extension), which has already cleared the challenge, so the same\nrequests succeed. There's no easyTable login: a restaurant is identified only\nby its `id` (a.k.a. `place`) from its widget URL\n`https://book.easytable.com/book/?id=<placeId>`.\n\nThis is the same data/actions the `easytable_*` MCP tools use, reached with\none-shot CLI calls instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli               # provides `fpx`\nfpx profile add easytable --domain easytable.com\nfpx pair -p easytable                        # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** extension installed (from\nhttps://github.com/nullnet-app/contextmint-bridge/releases — Chrome: load the\nchrome zip unpacked; Safari: ships inside the ContextMint app, no public download\nyet, so use Chrome for now — it is the renamed fetchproxy extension, same maintainer; source is public, so\nbuild it or verify the zip with its published `.sha256`), with an open\n`https://book.easytable.com/book/?id=<placeId>` tab left to finish loading\n(its Turnstile check solves itself), and Chrome **Site access** allowing\n`easytable.com`. Pairing persists after the first approval.\n\n## Core call\n\nReads are GET requests that return **HTML fragments** (not JSON) — the\nwidget loads them into a container div. Send the `X-Requested-With` header\nthe widget sends, and pipe the fragment to `grep`/`sed` rather than `jq`:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest'\n```\n\nReady-to-run URLs + extraction recipes for every read, plus the cancel write\nand the create/modify payload shape, are in\n`references/easytable-requests.md`. Exhaustive parsing lives in the repo at\n`src/parse.ts` / `docs/EASYTABLE-API.md` — the recipes here are compact\ngreps against the same markup those parsers target.\n\n## The one rule: resolve the `type` first\n\nDates and times are scoped to a booking area. Always list types first, take\nthe `type` id, then feed it to dates/times:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nSame pattern for cancelling: resolve the booking id via `cancel-search.asp`\n(by mobile number) before calling the cancel endpoint.\n\n## Creating or modifying a booking — not fully doable through fpx\n\n`easytable_create_booking`/`easytable_modify_booking` need a live Cloudflare\n**Turnstile token**, read from the widget's hidden\n`input[name=\"cf-turnstile-response\"]`. The MCP harvests it with a bespoke\n`read_dom` verb wired into *this repo's own* fetchproxy transport\n(`bridge-fetchproxy.ts`) — the stock `fpx` CLI has no DOM-read command (only\n`get`/`post-json`/`request`/`cookies`/`local-storage`/`session-storage`/\n`indexeddb`/`session`), so it cannot harvest that token.\n\nThe other three page-derived values (`bookingToken`, `cancellationtime`,\n`lcid`) ARE plain server-rendered text, so `fpx get` + `grep` gets them fine\n— see `references/easytable-requests.md` for the full payload shape and a\nmanual-token workaround (read the Turnstile input yourself from the browser\nconsole). Treat create/modify as a manual/hybrid flow, not a clean one-shot.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. Write endpoints answer HTTP 200 even on a *rejected*\n  booking (`Status:0` in the body) — always check the parsed `Status`, not\n  just the exit code.\n- `2` — bridge unavailable: extension not connected or pairing pending → run\n  `fpx pair -p easytable`, confirm an easytable tab is open.\n- `3` — bot wall: the tab hasn't cleared Cloudflare → open/refresh a\n  `book.easytable.com` tab and retry.\n- `4` — upstream non-2xx from easyTable.\n\n## Notes\n\n- No easyTable account or API key involved — this is the user's own action\n  on their own signed-in browser session.\n- `fpx health -p easytable` shows bridge connection state when a call fails.\n- Don't fire real create/cancel calls just to \"test\" the flow — a\n  confirmed write here books/cancels a real table at a real\n  restaurant.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.2.4:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"easytable-fpx\",\n  \"version\": \"1.2.4\",\n  \"publishedAt\": 1791168563231\n}\n\nFile v1.2.4:references/easytable-requests.md\n\n# easyTable requests for fpx\n\nReady-to-run URLs for `fpx get '<url>' -p easytable -H 'X-Requested-With: XMLHttpRequest'`\n(reads) and `fpx post-json`/`fpx get` (writes). All paths/params/field shapes\nare transcribed from `src/client.ts`, `src/parse.ts`, `src/payload.ts`, and\n`docs/EASYTABLE-API.md` — the same shapes the `easytable_*` MCP tools use.\n\nHost: `https://book.easytable.com`. Every URL below needs `id=<placeId>`\n(a.k.a. `place`) and `lang=<code>` (`en`, `se`, `da`, … — defaults to `en`).\nReplace `1fdfc` with a real restaurant id from a\n`book.easytable.com/book/?id=<id>` link.\n\nReads answer **HTML fragments**, not JSON — extraction below is `grep -oE`\nagainst the exact attributes `src/parse.ts` selects on. These are compact\ngreps, not a full parser; for anything beyond simple extraction, read\n`src/parse.ts`.\n\n---\n\n## 1. List booking areas/types (resolve this first)\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nThe numeric value after `type=` in the `href` is the `type` id every later\ncall needs.\n\n## 2. List bookable dates\n\nNeeds `type` (from §1) and `qty` (party size).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/calendar.asp?id=1fdfc&lang=en&type=13991&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*day[^\"]*\"[^>]*data-date=\"[0-9-]+\"[^>]*>'\n```\n\nA `class` containing `av` = available; `closed`/`ua` = not. `data-note`\n(when present) carries a note.\n\n## 3. List time slots\n\nNeeds `type`, a `date` (ISO `YYYY-MM-DD`, from §2), and `qty`. Slot values\nare **minute-of-day** (1035 → 17:15 — `hh=floor(m/60)`, `mm=m%60`).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/times.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*time[^\"]*\"[^>]*data-time=\"[0-9]+\"[^>]*>'\n```\n\n## 4. Find existing bookings by phone\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/cancel-search.asp?id=1fdfc&lang=en&mobile=%2B46701234567' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE 'data-booking=\"[^\"]+\"|data-id=\"[^\"]+\"|data-mobile=\"[^\"]+\"'\n```\n\nURL-encode the mobile (`+` → `%2B`). The booking id is on\n`data-booking`/`data-id` (`easytable-mcp` prefers `data-booking`, falling\nback to `data-id` — never `value`, which can be a button label).\n\n## 5. Cancel a booking (no Turnstile — plain tokenless GET)\n\n```sh\nfpx get 'https://book.easytable.com/user/ajax/json_cancel_booking.asp?place=1fdfc&mobile=%2B46701234567&booking=<bookingId>' \\\n  -p easytable\n```\n\nThe response is JSONP-wrapped (`callback([{Status,...}])`); unwrap and read\nwith:\n\n```sh\nsed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] | {Status, errHtml, successHtml, confirmUrl}'\n```\n\n`Status: 1` = cancelled; `Status: 0` = rejected (see `errHtml`).\n\n## 6. Create / modify a booking — payload shape (Turnstile caveat applies)\n\nHarvest the three plain-text page-derived values first — these ARE\nreachable via `fpx get` (they're server-rendered, unlike the Turnstile\ntoken):\n\n```sh\n# bookingToken + cancellationtime, from the confirm.asp fragment:\nfpx get 'https://book.easytable.com/book/ajax/confirm.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&time=1035&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' > /tmp/confirm.html\ngrep -oE 'bookingToken\\s*=\\s*\"[^\"]+\"' /tmp/confirm.html          # → {EB9351E5-...}\ngrep -oE 'id=\"cancellationtime\"[^>]*value=\"[0-9]+\"' /tmp/confirm.html  # → e.g. 180\n\n# lcid, from the widget page HTML (language-dependent: se→1053, en→2057):\nfpx get 'https://book.easytable.com/book/?id=1fdfc&lang=en' -p easytable \\\n  | grep -oE '\\blcid\\s*=\\s*\"?[0-9]+'\n```\n\nThe **Turnstile token cannot be harvested through `fpx`** — the CLI has no\nDOM-read verb. Manual workaround: with the widget tab open and loaded (past\nthe confirm step so Turnstile has rendered), open DevTools console on that\ntab and run:\n\n```js\ndocument.querySelector('input[name=\"cf-turnstile-response\"]').value\n```\n\nCopy that value in as `turnstileToken` below. It's single-use and expires in\n~300s, so harvest it right before the POST.\n\nPOST body — the full 32-key `dataObj` (the endpoint 500s on a partial\npayload; `time`/`persons` MUST be strings; omit `event`/`existing` when\nunused):\n\n```json\n{\n  \"place\": \"1fdfc\",\n  \"type\": \"13991\",\n  \"date\": \"2026-08-01\",\n  \"persons\": \"2\",\n  \"time\": \"1035\",\n  \"name\": \"Guest Name\",\n  \"mobile\": \"+46701234567\",\n  \"email\": \"\",\n  \"room\": \"\",\n  \"company\": \"\",\n  \"address\": \"\",\n  \"zip\": \"\",\n  \"city\": \"\",\n  \"country\": \"\",\n  \"comment\": \"\",\n  \"newsletter\": 0,\n  \"newsletterrelated\": 0,\n  \"lcid\": \"2057\",\n  \"lang\": \"en\",\n  \"ref\": \"\",\n  \"cancellationtime\": \"180\",\n  \"promocode\": \"\",\n  \"groupRequestTerms\": 0,\n  \"preorder\": {},\n  \"tags\": [],\n  \"amounttags\": [],\n  \"customFields\": [],\n  \"bookingInfo\": [],\n  \"bookingToken\": \"{EB9351E5-...}\",\n  \"botScore\": 0,\n  \"websitePot\": \"\",\n  \"turnstileToken\": \"<paste the harvested token>\"\n}\n```\n\n```sh\nfpx post-json 'https://book.easytable.com/user/ajax/json_booking.asp' @/tmp/booking.json -p easytable \\\n  | sed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] // .'\n```\n\nFor **modify**, POST the same shape plus `\"existing\": \"<existingBookingId>\"`\nto `https://book.easytable.com/user/ajax/json_modify_booking.asp`.\n\nA `Status: 0` response with the token/bookingToken/cancellationtime fields\nall present and non-empty means the request itself is malformed — re-check\nthe harvested values and the field types (`time`/`persons` as strings).\n\nFile v1.2.4:skill-card.md\n\n## Description:\n\nGuides agents using the fpx CLI to check easyTable availability, find bookings by phone, and cancel bookings through a paired browser tab.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and other users can check restaurant booking areas, dates, and times, find reservations by phone, and cancel bookings without running an MCP server. Creating or modifying bookings requires a manual browser step.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Agent-guided commands through a paired browser can change live restaurant bookings.\n\nMitigation: Review bridge pairing and require explicit human confirmation before cancellation, creation, or modification; do not test with real write requests.\n\nRisk: Creating or modifying bookings involves reuse of a browser anti-bot token.\n\nMitigation: Review the token-handling workflow before installation and use it only for the intended booking action.\n\n## Reference(s):\n\n- [easyTable request examples](references/easytable-requests.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [easytable-fpx release](https://clawhub.ai/chrischall/skills/easytable-fpx)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands]\n\n**Output Format:** [Markdown with shell command examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Read requests return HTML fragments; cancellations return JSONP. Creating or modifying bookings requires a manually obtained browser token.]\n\n## Skill Version(s):\n\n1.2.4 (source: ClawHub release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.3: 4 files, 6715 bytes\n\nFiles: references/easytable-requests.md (5701b), skill-card.md (2215b), SKILL.md (5044b), _meta.json (132b)\n\nFile v1.2.3:SKILL.md\n\n---\nname: easytable-fpx\ndescription: >-\n  Check restaurant availability and manage easyTable bookings\n  (book.easytable.com/book/?id=<placeId>) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the easytable-mcp server — list\n  booking areas/dates/times, look up a booking by phone, and cancel it, all\n  one-shot through a signed-in browser tab. Use when you want easyTable\n  access without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# easyTable via fpx (no MCP)\n\neasyTable fronts every `book.easytable.com` endpoint — reads and writes alike\n— with a Cloudflare check that 403s any plain `curl`/Node request. `fpx`\nroutes the request through the user's own signed-in browser tab (the\nContextMint Bridge extension), which has already cleared the challenge, so the same\nrequests succeed. There's no easyTable login: a restaurant is identified only\nby its `id` (a.k.a. `place`) from its widget URL\n`https://book.easytable.com/book/?id=<placeId>`.\n\nThis is the same data/actions the `easytable_*` MCP tools use, reached with\none-shot CLI calls instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli               # provides `fpx`\nfpx profile add easytable --domain easytable.com\nfpx pair -p easytable                        # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** extension installed (from\nhttps://github.com/nullnet-app/contextmint-bridge/releases — Chrome: load the\nchrome zip unpacked; Safari: ships inside the ContextMint app, no public download\nyet, so use Chrome for now — it is the renamed fetchproxy extension, same maintainer; source is public, so\nbuild it or verify the zip with its published `.sha256`), with an open\n`https://book.easytable.com/book/?id=<placeId>` tab left to finish loading\n(its Turnstile check solves itself), and Chrome **Site access** allowing\n`easytable.com`. Pairing persists after the first approval.\n\n## Core call\n\nReads are GET requests that return **HTML fragments** (not JSON) — the\nwidget loads them into a container div. Send the `X-Requested-With` header\nthe widget sends, and pipe the fragment to `grep`/`sed` rather than `jq`:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest'\n```\n\nReady-to-run URLs + extraction recipes for every read, plus the cancel write\nand the create/modify payload shape, are in\n`references/easytable-requests.md`. Exhaustive parsing lives in the repo at\n`src/parse.ts` / `docs/EASYTABLE-API.md` — the recipes here are compact\ngreps against the same markup those parsers target.\n\n## The one rule: resolve the `type` first\n\nDates and times are scoped to a booking area. Always list types first, take\nthe `type` id, then feed it to dates/times:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nSame pattern for cancelling: resolve the booking id via `cancel-search.asp`\n(by mobile number) before calling the cancel endpoint.\n\n## Creating or modifying a booking — not fully doable through fpx\n\n`easytable_create_booking`/`easytable_modify_booking` need a live Cloudflare\n**Turnstile token**, read from the widget's hidden\n`input[name=\"cf-turnstile-response\"]`. The MCP harvests it with a bespoke\n`read_dom` verb wired into *this repo's own* fetchproxy transport\n(`bridge-fetchproxy.ts`) — the stock `fpx` CLI has no DOM-read command (only\n`get`/`post-json`/`request`/`cookies`/`local-storage`/`session-storage`/\n`indexeddb`/`session`), so it cannot harvest that token.\n\nThe other three page-derived values (`bookingToken`, `cancellationtime`,\n`lcid`) ARE plain server-rendered text, so `fpx get` + `grep` gets them fine\n— see `references/easytable-requests.md` for the full payload shape and a\nmanual-token workaround (read the Turnstile input yourself from the browser\nconsole). Treat create/modify as a manual/hybrid flow, not a clean one-shot.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. Write endpoints answer HTTP 200 even on a *rejected*\n  booking (`Status:0` in the body) — always check the parsed `Status`, not\n  just the exit code.\n- `2` — bridge unavailable: extension not connected or pairing pending → run\n  `fpx pair -p easytable`, confirm an easytable tab is open.\n- `3` — bot wall: the tab hasn't cleared Cloudflare → open/refresh a\n  `book.easytable.com` tab and retry.\n- `4` — upstream non-2xx from easyTable.\n\n## Notes\n\n- No easyTable account or API key involved — this is the user's own action\n  on their own signed-in browser session.\n- `fpx health -p easytable` shows bridge connection state when a call fails.\n- Don't fire real create/cancel calls just to \"test\" the flow — a\n  confirmed write here books/cancels a real table at a real\n  restaurant.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.2.3:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"easytable-fpx\",\n  \"version\": \"1.2.3\",\n  \"publishedAt\": 1790991586892\n}\n\nFile v1.2.3:references/easytable-requests.md\n\n# easyTable requests for fpx\n\nReady-to-run URLs for `fpx get '<url>' -p easytable -H 'X-Requested-With: XMLHttpRequest'`\n(reads) and `fpx post-json`/`fpx get` (writes). All paths/params/field shapes\nare transcribed from `src/client.ts`, `src/parse.ts`, `src/payload.ts`, and\n`docs/EASYTABLE-API.md` — the same shapes the `easytable_*` MCP tools use.\n\nHost: `https://book.easytable.com`. Every URL below needs `id=<placeId>`\n(a.k.a. `place`) and `lang=<code>` (`en`, `se`, `da`, … — defaults to `en`).\nReplace `1fdfc` with a real restaurant id from a\n`book.easytable.com/book/?id=<id>` link.\n\nReads answer **HTML fragments**, not JSON — extraction below is `grep -oE`\nagainst the exact attributes `src/parse.ts` selects on. These are compact\ngreps, not a full parser; for anything beyond simple extraction, read\n`src/parse.ts`.\n\n---\n\n## 1. List booking areas/types (resolve this first)\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nThe numeric value after `type=` in the `href` is the `type` id every later\ncall needs.\n\n## 2. List bookable dates\n\nNeeds `type` (from §1) and `qty` (party size).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/calendar.asp?id=1fdfc&lang=en&type=13991&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*day[^\"]*\"[^>]*data-date=\"[0-9-]+\"[^>]*>'\n```\n\nA `class` containing `av` = available; `closed`/`ua` = not. `data-note`\n(when present) carries a note.\n\n## 3. List time slots\n\nNeeds `type`, a `date` (ISO `YYYY-MM-DD`, from §2), and `qty`. Slot values\nare **minute-of-day** (1035 → 17:15 — `hh=floor(m/60)`, `mm=m%60`).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/times.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*time[^\"]*\"[^>]*data-time=\"[0-9]+\"[^>]*>'\n```\n\n## 4. Find existing bookings by phone\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/cancel-search.asp?id=1fdfc&lang=en&mobile=%2B46701234567' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE 'data-booking=\"[^\"]+\"|data-id=\"[^\"]+\"|data-mobile=\"[^\"]+\"'\n```\n\nURL-encode the mobile (`+` → `%2B`). The booking id is on\n`data-booking`/`data-id` (`easytable-mcp` prefers `data-booking`, falling\nback to `data-id` — never `value`, which can be a button label).\n\n## 5. Cancel a booking (no Turnstile — plain tokenless GET)\n\n```sh\nfpx get 'https://book.easytable.com/user/ajax/json_cancel_booking.asp?place=1fdfc&mobile=%2B46701234567&booking=<bookingId>' \\\n  -p easytable\n```\n\nThe response is JSONP-wrapped (`callback([{Status,...}])`); unwrap and read\nwith:\n\n```sh\nsed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] | {Status, errHtml, successHtml, confirmUrl}'\n```\n\n`Status: 1` = cancelled; `Status: 0` = rejected (see `errHtml`).\n\n## 6. Create / modify a booking — payload shape (Turnstile caveat applies)\n\nHarvest the three plain-text page-derived values first — these ARE\nreachable via `fpx get` (they're server-rendered, unlike the Turnstile\ntoken):\n\n```sh\n# bookingToken + cancellationtime, from the confirm.asp fragment:\nfpx get 'https://book.easytable.com/book/ajax/confirm.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&time=1035&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' > /tmp/confirm.html\ngrep -oE 'bookingToken\\s*=\\s*\"[^\"]+\"' /tmp/confirm.html          # → {EB9351E5-...}\ngrep -oE 'id=\"cancellationtime\"[^>]*value=\"[0-9]+\"' /tmp/confirm.html  # → e.g. 180\n\n# lcid, from the widget page HTML (language-dependent: se→1053, en→2057):\nfpx get 'https://book.easytable.com/book/?id=1fdfc&lang=en' -p easytable \\\n  | grep -oE '\\blcid\\s*=\\s*\"?[0-9]+'\n```\n\nThe **Turnstile token cannot be harvested through `fpx`** — the CLI has no\nDOM-read verb. Manual workaround: with the widget tab open and loaded (past\nthe confirm step so Turnstile has rendered), open DevTools console on that\ntab and run:\n\n```js\ndocument.querySelector('input[name=\"cf-turnstile-response\"]').value\n```\n\nCopy that value in as `turnstileToken` below. It's single-use and expires in\n~300s, so harvest it right before the POST.\n\nPOST body — the full 32-key `dataObj` (the endpoint 500s on a partial\npayload; `time`/`persons` MUST be strings; omit `event`/`existing` when\nunused):\n\n```json\n{\n  \"place\": \"1fdfc\",\n  \"type\": \"13991\",\n  \"date\": \"2026-08-01\",\n  \"persons\": \"2\",\n  \"time\": \"1035\",\n  \"name\": \"Guest Name\",\n  \"mobile\": \"+46701234567\",\n  \"email\": \"\",\n  \"room\": \"\",\n  \"company\": \"\",\n  \"address\": \"\",\n  \"zip\": \"\",\n  \"city\": \"\",\n  \"country\": \"\",\n  \"comment\": \"\",\n  \"newsletter\": 0,\n  \"newsletterrelated\": 0,\n  \"lcid\": \"2057\",\n  \"lang\": \"en\",\n  \"ref\": \"\",\n  \"cancellationtime\": \"180\",\n  \"promocode\": \"\",\n  \"groupRequestTerms\": 0,\n  \"preorder\": {},\n  \"tags\": [],\n  \"amounttags\": [],\n  \"customFields\": [],\n  \"bookingInfo\": [],\n  \"bookingToken\": \"{EB9351E5-...}\",\n  \"botScore\": 0,\n  \"websitePot\": \"\",\n  \"turnstileToken\": \"<paste the harvested token>\"\n}\n```\n\n```sh\nfpx post-json 'https://book.easytable.com/user/ajax/json_booking.asp' @/tmp/booking.json -p easytable \\\n  | sed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] // .'\n```\n\nFor **modify**, POST the same shape plus `\"existing\": \"<existingBookingId>\"`\nto `https://book.easytable.com/user/ajax/json_modify_booking.asp`.\n\nA `Status: 0` response with the token/bookingToken/cancellationtime fields\nall present and non-empty means the request itself is malformed — re-check\nthe harvested values and the field types (`time`/`persons` as strings).\n\nFile v1.2.3:skill-card.md\n\n## Description:\n\nGuides agents through checking easyTable availability, finding bookings by phone, and cancelling reservations using the fpx CLI through a browser tab.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and other users can use this skill to check restaurant booking areas, dates, and times, look up an existing reservation, and cancel it without running an MCP server. Creating or modifying reservations requires a manual browser step.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Commands can change or cancel real restaurant reservations.\n\nMitigation: Require explicit user confirmation before any create, modify, or cancel command; do not test with real writes.\n\nRisk: Browser-bridged requests depend on a third-party extension and CLI running in the user's browser context.\n\nMitigation: Verify the extension and package source before installation and scope the browser profile to easytable.com.\n\nRisk: Booking details and short-lived Turnstile tokens can be exposed in commands or logs.\n\nMitigation: Keep phone numbers, booking details, and Turnstile tokens out of shared logs and transcripts.\n\n## Reference(s):\n\n- [easyTable request recipes](references/easytable-requests.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [ClawHub skill release](https://clawhub.ai/chrischall/skills/easytable-fpx)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance, Configuration instructions]\n\n**Output Format:** [Markdown with shell commands and example JSON]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Availability reads return HTML fragments; cancellation responses must be checked for success in the response body.]\n\n## Skill Version(s):\n\n1.2.3 (source: ClawHub release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.2: 4 files, 6672 bytes\n\nFiles: references/easytable-requests.md (5701b), skill-card.md (2236b), SKILL.md (5044b), _meta.json (132b)\n\nFile v1.2.2:SKILL.md\n\n---\nname: easytable-fpx\ndescription: >-\n  Check restaurant availability and manage easyTable bookings\n  (book.easytable.com/book/?id=<placeId>) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the easytable-mcp server — list\n  booking areas/dates/times, look up a booking by phone, and cancel it, all\n  one-shot through a signed-in browser tab. Use when you want easyTable\n  access without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# easyTable via fpx (no MCP)\n\neasyTable fronts every `book.easytable.com` endpoint — reads and writes alike\n— with a Cloudflare check that 403s any plain `curl`/Node request. `fpx`\nroutes the request through the user's own signed-in browser tab (the\nContextMint Bridge extension), which has already cleared the challenge, so the same\nrequests succeed. There's no easyTable login: a restaurant is identified only\nby its `id` (a.k.a. `place`) from its widget URL\n`https://book.easytable.com/book/?id=<placeId>`.\n\nThis is the same data/actions the `easytable_*` MCP tools use, reached with\none-shot CLI calls instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli               # provides `fpx`\nfpx profile add easytable --domain easytable.com\nfpx pair -p easytable                        # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** extension installed (from\nhttps://github.com/nullnet-app/contextmint-bridge/releases — Chrome: load the\nchrome zip unpacked; Safari: ships inside the ContextMint app, no public download\nyet, so use Chrome for now — it is the renamed fetchproxy extension, same maintainer; source is public, so\nbuild it or verify the zip with its published `.sha256`), with an open\n`https://book.easytable.com/book/?id=<placeId>` tab left to finish loading\n(its Turnstile check solves itself), and Chrome **Site access** allowing\n`easytable.com`. Pairing persists after the first approval.\n\n## Core call\n\nReads are GET requests that return **HTML fragments** (not JSON) — the\nwidget loads them into a container div. Send the `X-Requested-With` header\nthe widget sends, and pipe the fragment to `grep`/`sed` rather than `jq`:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest'\n```\n\nReady-to-run URLs + extraction recipes for every read, plus the cancel write\nand the create/modify payload shape, are in\n`references/easytable-requests.md`. Exhaustive parsing lives in the repo at\n`src/parse.ts` / `docs/EASYTABLE-API.md` — the recipes here are compact\ngreps against the same markup those parsers target.\n\n## The one rule: resolve the `type` first\n\nDates and times are scoped to a booking area. Always list types first, take\nthe `type` id, then feed it to dates/times:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nSame pattern for cancelling: resolve the booking id via `cancel-search.asp`\n(by mobile number) before calling the cancel endpoint.\n\n## Creating or modifying a booking — not fully doable through fpx\n\n`easytable_create_booking`/`easytable_modify_booking` need a live Cloudflare\n**Turnstile token**, read from the widget's hidden\n`input[name=\"cf-turnstile-response\"]`. The MCP harvests it with a bespoke\n`read_dom` verb wired into *this repo's own* fetchproxy transport\n(`bridge-fetchproxy.ts`) — the stock `fpx` CLI has no DOM-read command (only\n`get`/`post-json`/`request`/`cookies`/`local-storage`/`session-storage`/\n`indexeddb`/`session`), so it cannot harvest that token.\n\nThe other three page-derived values (`bookingToken`, `cancellationtime`,\n`lcid`) ARE plain server-rendered text, so `fpx get` + `grep` gets them fine\n— see `references/easytable-requests.md` for the full payload shape and a\nmanual-token workaround (read the Turnstile input yourself from the browser\nconsole). Treat create/modify as a manual/hybrid flow, not a clean one-shot.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. Write endpoints answer HTTP 200 even on a *rejected*\n  booking (`Status:0` in the body) — always check the parsed `Status`, not\n  just the exit code.\n- `2` — bridge unavailable: extension not connected or pairing pending → run\n  `fpx pair -p easytable`, confirm an easytable tab is open.\n- `3` — bot wall: the tab hasn't cleared Cloudflare → open/refresh a\n  `book.easytable.com` tab and retry.\n- `4` — upstream non-2xx from easyTable.\n\n## Notes\n\n- No easyTable account or API key involved — this is the user's own action\n  on their own signed-in browser session.\n- `fpx health -p easytable` shows bridge connection state when a call fails.\n- Don't fire real create/cancel calls just to \"test\" the flow — a\n  confirmed write here books/cancels a real table at a real\n  restaurant.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.2.2:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"easytable-fpx\",\n  \"version\": \"1.2.2\",\n  \"publishedAt\": 1790879069859\n}\n\nFile v1.2.2:references/easytable-requests.md\n\n# easyTable requests for fpx\n\nReady-to-run URLs for `fpx get '<url>' -p easytable -H 'X-Requested-With: XMLHttpRequest'`\n(reads) and `fpx post-json`/`fpx get` (writes). All paths/params/field shapes\nare transcribed from `src/client.ts`, `src/parse.ts`, `src/payload.ts`, and\n`docs/EASYTABLE-API.md` — the same shapes the `easytable_*` MCP tools use.\n\nHost: `https://book.easytable.com`. Every URL below needs `id=<placeId>`\n(a.k.a. `place`) and `lang=<code>` (`en`, `se`, `da`, … — defaults to `en`).\nReplace `1fdfc` with a real restaurant id from a\n`book.easytable.com/book/?id=<id>` link.\n\nReads answer **HTML fragments**, not JSON — extraction below is `grep -oE`\nagainst the exact attributes `src/parse.ts` selects on. These are compact\ngreps, not a full parser; for anything beyond simple extraction, read\n`src/parse.ts`.\n\n---\n\n## 1. List booking areas/types (resolve this first)\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nThe numeric value after `type=` in the `href` is the `type` id every later\ncall needs.\n\n## 2. List bookable dates\n\nNeeds `type` (from §1) and `qty` (party size).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/calendar.asp?id=1fdfc&lang=en&type=13991&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*day[^\"]*\"[^>]*data-date=\"[0-9-]+\"[^>]*>'\n```\n\nA `class` containing `av` = available; `closed`/`ua` = not. `data-note`\n(when present) carries a note.\n\n## 3. List time slots\n\nNeeds `type`, a `date` (ISO `YYYY-MM-DD`, from §2), and `qty`. Slot values\nare **minute-of-day** (1035 → 17:15 — `hh=floor(m/60)`, `mm=m%60`).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/times.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*time[^\"]*\"[^>]*data-time=\"[0-9]+\"[^>]*>'\n```\n\n## 4. Find existing bookings by phone\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/cancel-search.asp?id=1fdfc&lang=en&mobile=%2B46701234567' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE 'data-booking=\"[^\"]+\"|data-id=\"[^\"]+\"|data-mobile=\"[^\"]+\"'\n```\n\nURL-encode the mobile (`+` → `%2B`). The booking id is on\n`data-booking`/`data-id` (`easytable-mcp` prefers `data-booking`, falling\nback to `data-id` — never `value`, which can be a button label).\n\n## 5. Cancel a booking (no Turnstile — plain tokenless GET)\n\n```sh\nfpx get 'https://book.easytable.com/user/ajax/json_cancel_booking.asp?place=1fdfc&mobile=%2B46701234567&booking=<bookingId>' \\\n  -p easytable\n```\n\nThe response is JSONP-wrapped (`callback([{Status,...}])`); unwrap and read\nwith:\n\n```sh\nsed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] | {Status, errHtml, successHtml, confirmUrl}'\n```\n\n`Status: 1` = cancelled; `Status: 0` = rejected (see `errHtml`).\n\n## 6. Create / modify a booking — payload shape (Turnstile caveat applies)\n\nHarvest the three plain-text page-derived values first — these ARE\nreachable via `fpx get` (they're server-rendered, unlike the Turnstile\ntoken):\n\n```sh\n# bookingToken + cancellationtime, from the confirm.asp fragment:\nfpx get 'https://book.easytable.com/book/ajax/confirm.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&time=1035&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' > /tmp/confirm.html\ngrep -oE 'bookingToken\\s*=\\s*\"[^\"]+\"' /tmp/confirm.html          # → {EB9351E5-...}\ngrep -oE 'id=\"cancellationtime\"[^>]*value=\"[0-9]+\"' /tmp/confirm.html  # → e.g. 180\n\n# lcid, from the widget page HTML (language-dependent: se→1053, en→2057):\nfpx get 'https://book.easytable.com/book/?id=1fdfc&lang=en' -p easytable \\\n  | grep -oE '\\blcid\\s*=\\s*\"?[0-9]+'\n```\n\nThe **Turnstile token cannot be harvested through `fpx`** — the CLI has no\nDOM-read verb. Manual workaround: with the widget tab open and loaded (past\nthe confirm step so Turnstile has rendered), open DevTools console on that\ntab and run:\n\n```js\ndocument.querySelector('input[name=\"cf-turnstile-response\"]').value\n```\n\nCopy that value in as `turnstileToken` below. It's single-use and expires in\n~300s, so harvest it right before the POST.\n\nPOST body — the full 32-key `dataObj` (the endpoint 500s on a partial\npayload; `time`/`persons` MUST be strings; omit `event`/`existing` when\nunused):\n\n```json\n{\n  \"place\": \"1fdfc\",\n  \"type\": \"13991\",\n  \"date\": \"2026-08-01\",\n  \"persons\": \"2\",\n  \"time\": \"1035\",\n  \"name\": \"Guest Name\",\n  \"mobile\": \"+46701234567\",\n  \"email\": \"\",\n  \"room\": \"\",\n  \"company\": \"\",\n  \"address\": \"\",\n  \"zip\": \"\",\n  \"city\": \"\",\n  \"country\": \"\",\n  \"comment\": \"\",\n  \"newsletter\": 0,\n  \"newsletterrelated\": 0,\n  \"lcid\": \"2057\",\n  \"lang\": \"en\",\n  \"ref\": \"\",\n  \"cancellationtime\": \"180\",\n  \"promocode\": \"\",\n  \"groupRequestTerms\": 0,\n  \"preorder\": {},\n  \"tags\": [],\n  \"amounttags\": [],\n  \"customFields\": [],\n  \"bookingInfo\": [],\n  \"bookingToken\": \"{EB9351E5-...}\",\n  \"botScore\": 0,\n  \"websitePot\": \"\",\n  \"turnstileToken\": \"<paste the harvested token>\"\n}\n```\n\n```sh\nfpx post-json 'https://book.easytable.com/user/ajax/json_booking.asp' @/tmp/booking.json -p easytable \\\n  | sed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] // .'\n```\n\nFor **modify**, POST the same shape plus `\"existing\": \"<existingBookingId>\"`\nto `https://book.easytable.com/user/ajax/json_modify_booking.asp`.\n\nA `Status: 0` response with the token/bookingToken/cancellationtime fields\nall present and non-empty means the request itself is malformed — re-check\nthe harvested values and the field types (`time`/`persons` as strings).\n\nFile v1.2.2:skill-card.md\n\n## Description:\n\nHelps agents check easyTable restaurant availability, find bookings by phone number, and cancel reservations through a paired browser using fpx instead of an MCP server.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and other authorized users can use an agent to inspect restaurant booking areas, dates, and times, look up their reservations, and cancel them without running an MCP server. Creating or changing a booking requires a manual browser step.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Booking lookup by phone number can disclose personal reservation details.\n\nMitigation: Look up only reservations you are authorized to manage and avoid saving phone numbers or booking details in reusable files or shell history.\n\nRisk: Cancel, create, and modify commands can change real restaurant reservations.\n\nMitigation: Confirm the reservation and intended action before any write; never run write examples as tests.\n\nRisk: Live requests rely on a paired browser extension and fpx CLI.\n\nMitigation: Review these tools before installation and use a browser session you control.\n\n## Reference(s):\n\n- [easyTable requests for fpx](references/easytable-requests.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [easytable-fpx on ClawHub](https://clawhub.ai/chrischall/skills/easytable-fpx)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Configuration instructions, Guidance]\n\n**Output Format:** [Markdown with shell command examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Availability and booking lookups are read-only; cancellation changes a live reservation. Creating or modifying a booking requires a manual browser step.]\n\n## Skill Version(s):\n\n1.2.2 (source: ClawHub release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.1: 4 files, 6712 bytes\n\nFiles: references/easytable-requests.md (5701b), skill-card.md (2301b), SKILL.md (5044b), _meta.json (132b)\n\nFile v1.2.1:SKILL.md\n\n---\nname: easytable-fpx\ndescription: >-\n  Check restaurant availability and manage easyTable bookings\n  (book.easytable.com/book/?id=<placeId>) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the easytable-mcp server — list\n  booking areas/dates/times, look up a booking by phone, and cancel it, all\n  one-shot through a signed-in browser tab. Use when you want easyTable\n  access without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# easyTable via fpx (no MCP)\n\neasyTable fronts every `book.easytable.com` endpoint — reads and writes alike\n— with a Cloudflare check that 403s any plain `curl`/Node request. `fpx`\nroutes the request through the user's own signed-in browser tab (the\nContextMint Bridge extension), which has already cleared the challenge, so the same\nrequests succeed. There's no easyTable login: a restaurant is identified only\nby its `id` (a.k.a. `place`) from its widget URL\n`https://book.easytable.com/book/?id=<placeId>`.\n\nThis is the same data/actions the `easytable_*` MCP tools use, reached with\none-shot CLI calls instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli               # provides `fpx`\nfpx profile add easytable --domain easytable.com\nfpx pair -p easytable                        # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** extension installed (from\nhttps://github.com/nullnet-app/contextmint-bridge/releases — Chrome: load the\nchrome zip unpacked; Safari: ships inside the ContextMint app, no public download\nyet, so use Chrome for now — it is the renamed fetchproxy extension, same maintainer; source is public, so\nbuild it or verify the zip with its published `.sha256`), with an open\n`https://book.easytable.com/book/?id=<placeId>` tab left to finish loading\n(its Turnstile check solves itself), and Chrome **Site access** allowing\n`easytable.com`. Pairing persists after the first approval.\n\n## Core call\n\nReads are GET requests that return **HTML fragments** (not JSON) — the\nwidget loads them into a container div. Send the `X-Requested-With` header\nthe widget sends, and pipe the fragment to `grep`/`sed` rather than `jq`:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest'\n```\n\nReady-to-run URLs + extraction recipes for every read, plus the cancel write\nand the create/modify payload shape, are in\n`references/easytable-requests.md`. Exhaustive parsing lives in the repo at\n`src/parse.ts` / `docs/EASYTABLE-API.md` — the recipes here are compact\ngreps against the same markup those parsers target.\n\n## The one rule: resolve the `type` first\n\nDates and times are scoped to a booking area. Always list types first, take\nthe `type` id, then feed it to dates/times:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nSame pattern for cancelling: resolve the booking id via `cancel-search.asp`\n(by mobile number) before calling the cancel endpoint.\n\n## Creating or modifying a booking — not fully doable through fpx\n\n`easytable_create_booking`/`easytable_modify_booking` need a live Cloudflare\n**Turnstile token**, read from the widget's hidden\n`input[name=\"cf-turnstile-response\"]`. The MCP harvests it with a bespoke\n`read_dom` verb wired into *this repo's own* fetchproxy transport\n(`bridge-fetchproxy.ts`) — the stock `fpx` CLI has no DOM-read command (only\n`get`/`post-json`/`request`/`cookies`/`local-storage`/`session-storage`/\n`indexeddb`/`session`), so it cannot harvest that token.\n\nThe other three page-derived values (`bookingToken`, `cancellationtime`,\n`lcid`) ARE plain server-rendered text, so `fpx get` + `grep` gets them fine\n— see `references/easytable-requests.md` for the full payload shape and a\nmanual-token workaround (read the Turnstile input yourself from the browser\nconsole). Treat create/modify as a manual/hybrid flow, not a clean one-shot.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. Write endpoints answer HTTP 200 even on a *rejected*\n  booking (`Status:0` in the body) — always check the parsed `Status`, not\n  just the exit code.\n- `2` — bridge unavailable: extension not connected or pairing pending → run\n  `fpx pair -p easytable`, confirm an easytable tab is open.\n- `3` — bot wall: the tab hasn't cleared Cloudflare → open/refresh a\n  `book.easytable.com` tab and retry.\n- `4` — upstream non-2xx from easyTable.\n\n## Notes\n\n- No easyTable account or API key involved — this is the user's own action\n  on their own signed-in browser session.\n- `fpx health -p easytable` shows bridge connection state when a call fails.\n- Don't fire real create/cancel calls just to \"test\" the flow — a\n  confirmed write here books/cancels a real table at a real\n  restaurant.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.2.1:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"easytable-fpx\",\n  \"version\": \"1.2.1\",\n  \"publishedAt\": 1790603858828\n}\n\nFile v1.2.1:references/easytable-requests.md\n\n# easyTable requests for fpx\n\nReady-to-run URLs for `fpx get '<url>' -p easytable -H 'X-Requested-With: XMLHttpRequest'`\n(reads) and `fpx post-json`/`fpx get` (writes). All paths/params/field shapes\nare transcribed from `src/client.ts`, `src/parse.ts`, `src/payload.ts`, and\n`docs/EASYTABLE-API.md` — the same shapes the `easytable_*` MCP tools use.\n\nHost: `https://book.easytable.com`. Every URL below needs `id=<placeId>`\n(a.k.a. `place`) and `lang=<code>` (`en`, `se`, `da`, … — defaults to `en`).\nReplace `1fdfc` with a real restaurant id from a\n`book.easytable.com/book/?id=<id>` link.\n\nReads answer **HTML fragments**, not JSON — extraction below is `grep -oE`\nagainst the exact attributes `src/parse.ts` selects on. These are compact\ngreps, not a full parser; for anything beyond simple extraction, read\n`src/parse.ts`.\n\n---\n\n## 1. List booking areas/types (resolve this first)\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nThe numeric value after `type=` in the `href` is the `type` id every later\ncall needs.\n\n## 2. List bookable dates\n\nNeeds `type` (from §1) and `qty` (party size).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/calendar.asp?id=1fdfc&lang=en&type=13991&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*day[^\"]*\"[^>]*data-date=\"[0-9-]+\"[^>]*>'\n```\n\nA `class` containing `av` = available; `closed`/`ua` = not. `data-note`\n(when present) carries a note.\n\n## 3. List time slots\n\nNeeds `type`, a `date` (ISO `YYYY-MM-DD`, from §2), and `qty`. Slot values\nare **minute-of-day** (1035 → 17:15 — `hh=floor(m/60)`, `mm=m%60`).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/times.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*time[^\"]*\"[^>]*data-time=\"[0-9]+\"[^>]*>'\n```\n\n## 4. Find existing bookings by phone\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/cancel-search.asp?id=1fdfc&lang=en&mobile=%2B46701234567' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE 'data-booking=\"[^\"]+\"|data-id=\"[^\"]+\"|data-mobile=\"[^\"]+\"'\n```\n\nURL-encode the mobile (`+` → `%2B`). The booking id is on\n`data-booking`/`data-id` (`easytable-mcp` prefers `data-booking`, falling\nback to `data-id` — never `value`, which can be a button label).\n\n## 5. Cancel a booking (no Turnstile — plain tokenless GET)\n\n```sh\nfpx get 'https://book.easytable.com/user/ajax/json_cancel_booking.asp?place=1fdfc&mobile=%2B46701234567&booking=<bookingId>' \\\n  -p easytable\n```\n\nThe response is JSONP-wrapped (`callback([{Status,...}])`); unwrap and read\nwith:\n\n```sh\nsed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] | {Status, errHtml, successHtml, confirmUrl}'\n```\n\n`Status: 1` = cancelled; `Status: 0` = rejected (see `errHtml`).\n\n## 6. Create / modify a booking — payload shape (Turnstile caveat applies)\n\nHarvest the three plain-text page-derived values first — these ARE\nreachable via `fpx get` (they're server-rendered, unlike the Turnstile\ntoken):\n\n```sh\n# bookingToken + cancellationtime, from the confirm.asp fragment:\nfpx get 'https://book.easytable.com/book/ajax/confirm.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&time=1035&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' > /tmp/confirm.html\ngrep -oE 'bookingToken\\s*=\\s*\"[^\"]+\"' /tmp/confirm.html          # → {EB9351E5-...}\ngrep -oE 'id=\"cancellationtime\"[^>]*value=\"[0-9]+\"' /tmp/confirm.html  # → e.g. 180\n\n# lcid, from the widget page HTML (language-dependent: se→1053, en→2057):\nfpx get 'https://book.easytable.com/book/?id=1fdfc&lang=en' -p easytable \\\n  | grep -oE '\\blcid\\s*=\\s*\"?[0-9]+'\n```\n\nThe **Turnstile token cannot be harvested through `fpx`** — the CLI has no\nDOM-read verb. Manual workaround: with the widget tab open and loaded (past\nthe confirm step so Turnstile has rendered), open DevTools console on that\ntab and run:\n\n```js\ndocument.querySelector('input[name=\"cf-turnstile-response\"]').value\n```\n\nCopy that value in as `turnstileToken` below. It's single-use and expires in\n~300s, so harvest it right before the POST.\n\nPOST body — the full 32-key `dataObj` (the endpoint 500s on a partial\npayload; `time`/`persons` MUST be strings; omit `event`/`existing` when\nunused):\n\n```json\n{\n  \"place\": \"1fdfc\",\n  \"type\": \"13991\",\n  \"date\": \"2026-08-01\",\n  \"persons\": \"2\",\n  \"time\": \"1035\",\n  \"name\": \"Guest Name\",\n  \"mobile\": \"+46701234567\",\n  \"email\": \"\",\n  \"room\": \"\",\n  \"company\": \"\",\n  \"address\": \"\",\n  \"zip\": \"\",\n  \"city\": \"\",\n  \"country\": \"\",\n  \"comment\": \"\",\n  \"newsletter\": 0,\n  \"newsletterrelated\": 0,\n  \"lcid\": \"2057\",\n  \"lang\": \"en\",\n  \"ref\": \"\",\n  \"cancellationtime\": \"180\",\n  \"promocode\": \"\",\n  \"groupRequestTerms\": 0,\n  \"preorder\": {},\n  \"tags\": [],\n  \"amounttags\": [],\n  \"customFields\": [],\n  \"bookingInfo\": [],\n  \"bookingToken\": \"{EB9351E5-...}\",\n  \"botScore\": 0,\n  \"websitePot\": \"\",\n  \"turnstileToken\": \"<paste the harvested token>\"\n}\n```\n\n```sh\nfpx post-json 'https://book.easytable.com/user/ajax/json_booking.asp' @/tmp/booking.json -p easytable \\\n  | sed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] // .'\n```\n\nFor **modify**, POST the same shape plus `\"existing\": \"<existingBookingId>\"`\nto `https://book.easytable.com/user/ajax/json_modify_booking.asp`.\n\nA `Status: 0` response with the token/bookingToken/cancellationtime fields\nall present and non-empty means the request itself is malformed — re-check\nthe harvested values and the field types (`time`/`persons` as strings).\n\nFile v1.2.1:skill-card.md\n\n## Description:\n\nGuides agents to check easyTable availability, find and cancel reservations, and prepare manual-assisted booking requests using the fpx CLI and a paired browser tab.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nPeople and developers using easyTable can check restaurant booking areas, dates, and times, look up reservations by phone number, and cancel bookings through their paired browser session without running an MCP server. Creating or modifying a booking requires a manual step.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Create, modify, and cancel commands can change real restaurant bookings.\n\nMitigation: Confirm the restaurant, reservation, and intended action before executing a write; do not test with live booking changes, and check the returned status.\n\nRisk: The CLI and browser extension act through the user's easyTable browser session.\n\nMitigation: Verify the external CLI and extension sources before installation and pairing, and only pair a browser session you intend to use.\n\nRisk: The manual Turnstile-token workaround may have site-policy implications.\n\nMitigation: Avoid the workaround unless you understand the implications; treat create and modify as manual-assisted rather than one-shot operations.\n\n## Reference(s):\n\n- [easytable-fpx release](https://clawhub.ai/chrischall/skills/easytable-fpx)\n- [easyTable request recipes](references/easytable-requests.md)\n- [ContextMint Bridge extension releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance]\n\n**Output Format:** [Markdown with shell command examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Availability responses include HTML fragments; booking actions require checking the response status.]\n\n## Skill Version(s):\n\n1.2.1 (source: ClawHub release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.2.0: 4 files, 6587 bytes\n\nFiles: references/easytable-requests.md (5701b), skill-card.md (2409b), SKILL.md (4689b), _meta.json (132b)\n\nFile v1.2.0:SKILL.md\n\n---\nname: easytable-fpx\ndescription: >-\n  Check restaurant availability and manage easyTable bookings\n  (book.easytable.com/book/?id=<placeId>) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the easytable-mcp server — list\n  booking areas/dates/times, look up a booking by phone, and cancel it, all\n  one-shot through a signed-in browser tab. Use when you want easyTable\n  access without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# easyTable via fpx (no MCP)\n\neasyTable fronts every `book.easytable.com` endpoint — reads and writes alike\n— with a Cloudflare check that 403s any plain `curl`/Node request. `fpx`\nroutes the request through the user's own signed-in browser tab (the\nTransporter extension), which has already cleared the challenge, so the same\nrequests succeed. There's no easyTable login: a restaurant is identified only\nby its `id` (a.k.a. `place`) from its widget URL\n`https://book.easytable.com/book/?id=<placeId>`.\n\nThis is the same data/actions the `easytable_*` MCP tools use, reached with\none-shot CLI calls instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli               # provides `fpx`\nfpx profile add easytable --domain easytable.com\nfpx pair -p easytable                        # prints a pair code → approve in Transporter\n```\n\nRequirements: the **Transporter** extension installed, with an open\n`https://book.easytable.com/book/?id=<placeId>` tab left to finish loading\n(its Turnstile check solves itself), and Chrome **Site access** allowing\n`easytable.com`. Pairing persists after the first approval.\n\n## Core call\n\nReads are GET requests that return **HTML fragments** (not JSON) — the\nwidget loads them into a container div. Send the `X-Requested-With` header\nthe widget sends, and pipe the fragment to `grep`/`sed` rather than `jq`:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest'\n```\n\nReady-to-run URLs + extraction recipes for every read, plus the cancel write\nand the create/modify payload shape, are in\n`references/easytable-requests.md`. Exhaustive parsing lives in the repo at\n`src/parse.ts` / `docs/EASYTABLE-API.md` — the recipes here are compact\ngreps against the same markup those parsers target.\n\n## The one rule: resolve the `type` first\n\nDates and times are scoped to a booking area. Always list types first, take\nthe `type` id, then feed it to dates/times:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nSame pattern for cancelling: resolve the booking id via `cancel-search.asp`\n(by mobile number) before calling the cancel endpoint.\n\n## Creating or modifying a booking — not fully doable through fpx\n\n`easytable_create_booking`/`easytable_modify_booking` need a live Cloudflare\n**Turnstile token**, read from the widget's hidden\n`input[name=\"cf-turnstile-response\"]`. The MCP harvests it with a bespoke\n`read_dom` verb wired into *this repo's own* fetchproxy transport\n(`bridge-fetchproxy.ts`) — the stock `fpx` CLI has no DOM-read command (only\n`get`/`post-json`/`request`/`cookies`/`local-storage`/`session-storage`/\n`indexeddb`/`session`), so it cannot harvest that token.\n\nThe other three page-derived values (`bookingToken`, `cancellationtime`,\n`lcid`) ARE plain server-rendered text, so `fpx get` + `grep` gets them fine\n— see `references/easytable-requests.md` for the full payload shape and a\nmanual-token workaround (read the Turnstile input yourself from the browser\nconsole). Treat create/modify as a manual/hybrid flow, not a clean one-shot.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. Write endpoints answer HTTP 200 even on a *rejected*\n  booking (`Status:0` in the body) — always check the parsed `Status`, not\n  just the exit code.\n- `2` — bridge unavailable: extension not connected or pairing pending → run\n  `fpx pair -p easytable`, confirm an easytable tab is open.\n- `3` — bot wall: the tab hasn't cleared Cloudflare → open/refresh a\n  `book.easytable.com` tab and retry.\n- `4` — upstream non-2xx from easyTable.\n\n## Notes\n\n- No easyTable account or API key involved — this is the user's own action\n  on their own signed-in browser session.\n- `fpx health -p easytable` shows bridge connection state when a call fails.\n- Don't fire real create/cancel calls just to \"test\" the flow — a\n  confirmed write here books/cancels a real table at a real\n  restaurant.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.2.0:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"easytable-fpx\",\n  \"version\": \"1.2.0\",\n  \"publishedAt\": 1790262742474\n}\n\nFile v1.2.0:references/easytable-requests.md\n\n# easyTable requests for fpx\n\nReady-to-run URLs for `fpx get '<url>' -p easytable -H 'X-Requested-With: XMLHttpRequest'`\n(reads) and `fpx post-json`/`fpx get` (writes). All paths/params/field shapes\nare transcribed from `src/client.ts`, `src/parse.ts`, `src/payload.ts`, and\n`docs/EASYTABLE-API.md` — the same shapes the `easytable_*` MCP tools use.\n\nHost: `https://book.easytable.com`. Every URL below needs `id=<placeId>`\n(a.k.a. `place`) and `lang=<code>` (`en`, `se`, `da`, … — defaults to `en`).\nReplace `1fdfc` with a real restaurant id from a\n`book.easytable.com/book/?id=<id>` link.\n\nReads answer **HTML fragments**, not JSON — extraction below is `grep -oE`\nagainst the exact attributes `src/parse.ts` selects on. These are compact\ngreps, not a full parser; for anything beyond simple extraction, read\n`src/parse.ts`.\n\n---\n\n## 1. List booking areas/types (resolve this first)\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nThe numeric value after `type=` in the `href` is the `type` id every later\ncall needs.\n\n## 2. List bookable dates\n\nNeeds `type` (from §1) and `qty` (party size).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/calendar.asp?id=1fdfc&lang=en&type=13991&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*day[^\"]*\"[^>]*data-date=\"[0-9-]+\"[^>]*>'\n```\n\nA `class` containing `av` = available; `closed`/`ua` = not. `data-note`\n(when present) carries a note.\n\n## 3. List time slots\n\nNeeds `type`, a `date` (ISO `YYYY-MM-DD`, from §2), and `qty`. Slot values\nare **minute-of-day** (1035 → 17:15 — `hh=floor(m/60)`, `mm=m%60`).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/times.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*time[^\"]*\"[^>]*data-time=\"[0-9]+\"[^>]*>'\n```\n\n## 4. Find existing bookings by phone\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/cancel-search.asp?id=1fdfc&lang=en&mobile=%2B46701234567' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE 'data-booking=\"[^\"]+\"|data-id=\"[^\"]+\"|data-mobile=\"[^\"]+\"'\n```\n\nURL-encode the mobile (`+` → `%2B`). The booking id is on\n`data-booking`/`data-id` (`easytable-mcp` prefers `data-booking`, falling\nback to `data-id` — never `value`, which can be a button label).\n\n## 5. Cancel a booking (no Turnstile — plain tokenless GET)\n\n```sh\nfpx get 'https://book.easytable.com/user/ajax/json_cancel_booking.asp?place=1fdfc&mobile=%2B46701234567&booking=<bookingId>' \\\n  -p easytable\n```\n\nThe response is JSONP-wrapped (`callback([{Status,...}])`); unwrap and read\nwith:\n\n```sh\nsed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] | {Status, errHtml, successHtml, confirmUrl}'\n```\n\n`Status: 1` = cancelled; `Status: 0` = rejected (see `errHtml`).\n\n## 6. Create / modify a booking — payload shape (Turnstile caveat applies)\n\nHarvest the three plain-text page-derived values first — these ARE\nreachable via `fpx get` (they're server-rendered, unlike the Turnstile\ntoken):\n\n```sh\n# bookingToken + cancellationtime, from the confirm.asp fragment:\nfpx get 'https://book.easytable.com/book/ajax/confirm.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&time=1035&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' > /tmp/confirm.html\ngrep -oE 'bookingToken\\s*=\\s*\"[^\"]+\"' /tmp/confirm.html          # → {EB9351E5-...}\ngrep -oE 'id=\"cancellationtime\"[^>]*value=\"[0-9]+\"' /tmp/confirm.html  # → e.g. 180\n\n# lcid, from the widget page HTML (language-dependent: se→1053, en→2057):\nfpx get 'https://book.easytable.com/book/?id=1fdfc&lang=en' -p easytable \\\n  | grep -oE '\\blcid\\s*=\\s*\"?[0-9]+'\n```\n\nThe **Turnstile token cannot be harvested through `fpx`** — the CLI has no\nDOM-read verb. Manual workaround: with the widget tab open and loaded (past\nthe confirm step so Turnstile has rendered), open DevTools console on that\ntab and run:\n\n```js\ndocument.querySelector('input[name=\"cf-turnstile-response\"]').value\n```\n\nCopy that value in as `turnstileToken` below. It's single-use and expires in\n~300s, so harvest it right before the POST.\n\nPOST body — the full 32-key `dataObj` (the endpoint 500s on a partial\npayload; `time`/`persons` MUST be strings; omit `event`/`existing` when\nunused):\n\n```json\n{\n  \"place\": \"1fdfc\",\n  \"type\": \"13991\",\n  \"date\": \"2026-08-01\",\n  \"persons\": \"2\",\n  \"time\": \"1035\",\n  \"name\": \"Guest Name\",\n  \"mobile\": \"+46701234567\",\n  \"email\": \"\",\n  \"room\": \"\",\n  \"company\": \"\",\n  \"address\": \"\",\n  \"zip\": \"\",\n  \"city\": \"\",\n  \"country\": \"\",\n  \"comment\": \"\",\n  \"newsletter\": 0,\n  \"newsletterrelated\": 0,\n  \"lcid\": \"2057\",\n  \"lang\": \"en\",\n  \"ref\": \"\",\n  \"cancellationtime\": \"180\",\n  \"promocode\": \"\",\n  \"groupRequestTerms\": 0,\n  \"preorder\": {},\n  \"tags\": [],\n  \"amounttags\": [],\n  \"customFields\": [],\n  \"bookingInfo\": [],\n  \"bookingToken\": \"{EB9351E5-...}\",\n  \"botScore\": 0,\n  \"websitePot\": \"\",\n  \"turnstileToken\": \"<paste the harvested token>\"\n}\n```\n\n```sh\nfpx post-json 'https://book.easytable.com/user/ajax/json_booking.asp' @/tmp/booking.json -p easytable \\\n  | sed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] // .'\n```\n\nFor **modify**, POST the same shape plus `\"existing\": \"<existingBookingId>\"`\nto `https://book.easytable.com/user/ajax/json_modify_booking.asp`.\n\nA `Status: 0` response with the token/bookingToken/cancellationtime fields\nall present and non-empty means the request itself is malformed — re-check\nthe harvested values and the field types (`time`/`persons` as strings).\n\nFile v1.2.0:skill-card.md\n\n## Description:\n\nCheck restaurant availability and manage easyTable bookings from a shell with the fpx CLI through a signed-in browser tab.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and technical operators use this skill to query easyTable restaurant availability and manage reservations from shell workflows without running the easytable MCP server. The workflow can perform real booking changes, so write actions should be reviewed before execution.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Confirmed create, modify, or cancel operations can change real restaurant reservations.\n\nMitigation: Require explicit user confirmation before any write action and do not run real create or cancel calls as tests.\n\nRisk: The workflow can use browser-backed session access and booking details such as phone numbers.\n\nMitigation: Use it only with the user's own easyTable session and booking details, and avoid handling other people's reservation information.\n\nRisk: Write endpoints can return HTTP 200 even when a booking action is rejected.\n\nMitigation: Inspect the parsed Status field and response body before treating a write action as successful.\n\nRisk: Create and modify flows require manual Turnstile token handling that is time-limited and easy to misuse.\n\nMitigation: Harvest the token only immediately before the POST and re-check all payload fields before submission.\n\n## Reference(s):\n\n- [easyTable request recipes](references/easytable-requests.md)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/easytable-fpx)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, Configuration, Code]\n\n**Output Format:** [Markdown with shell command examples and JSON payload examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands operate through the user's browser-backed easyTable session and may return HTML fragments or JSONP-wrapped responses.]\n\n## Skill Version(s):\n\n1.2.0 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.3: 4 files, 6588 bytes\n\nFiles: references/easytable-requests.md (5701b), skill-card.md (2472b), SKILL.md (4701b), _meta.json (132b)\n\nFile v1.1.3:SKILL.md\n\n---\nname: easytable-fpx\ndescription: >-\n  Check restaurant availability and manage easyTable bookings\n  (book.easytable.com/book/?id=<placeId>) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the easytable-mcp server — list\n  booking areas/dates/times, look up a booking by phone, and cancel it, all\n  one-shot through a signed-in browser tab. Use when you want easyTable\n  access without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# easyTable via fpx (no MCP)\n\neasyTable fronts every `book.easytable.com` endpoint — reads and writes alike\n— with a Cloudflare check that 403s any plain `curl`/Node request. `fpx`\nroutes the request through the user's own signed-in browser tab (the\nTransporter extension), which has already cleared the challenge, so the same\nrequests succeed. There's no easyTable login: a restaurant is identified only\nby its `id` (a.k.a. `place`) from its widget URL\n`https://book.easytable.com/book/?id=<placeId>`.\n\nThis is the same data/actions the `easytable_*` MCP tools use, reached with\none-shot CLI calls instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli               # provides `fpx`\nfpx profile add easytable --domain easytable.com\nfpx pair -p easytable                        # prints a pair code → approve in Transporter\n```\n\nRequirements: the **Transporter** extension installed, with an open\n`https://book.easytable.com/book/?id=<placeId>` tab left to finish loading\n(its Turnstile check solves itself), and Chrome **Site access** allowing\n`easytable.com`. Pairing persists after the first approval.\n\n## Core call\n\nReads are GET requests that return **HTML fragments** (not JSON) — the\nwidget loads them into a container div. Send the `X-Requested-With` header\nthe widget sends, and pipe the fragment to `grep`/`sed` rather than `jq`:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest'\n```\n\nReady-to-run URLs + extraction recipes for every read, plus the cancel write\nand the create/modify payload shape, are in\n`references/easytable-requests.md`. Exhaustive parsing lives in the repo at\n`src/parse.ts` / `docs/EASYTABLE-API.md` — the recipes here are compact\ngreps against the same markup those parsers target.\n\n## The one rule: resolve the `type` first\n\nDates and times are scoped to a booking area. Always list types first, take\nthe `type` id, then feed it to dates/times:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nSame pattern for cancelling: resolve the booking id via `cancel-search.asp`\n(by mobile number) before calling the cancel endpoint.\n\n## Creating or modifying a booking — not fully doable through fpx\n\n`easytable_create_booking`/`easytable_modify_booking` need a live Cloudflare\n**Turnstile token**, read from the widget's hidden\n`input[name=\"cf-turnstile-response\"]`. The MCP harvests it with a bespoke\n`read_dom` verb wired into *this repo's own* fetchproxy transport\n(`bridge-fetchproxy.ts`) — the stock `fpx` CLI has no DOM-read command (only\n`get`/`post-json`/`request`/`cookies`/`local-storage`/`session-storage`/\n`indexeddb`/`session`), so it cannot harvest that token.\n\nThe other three page-derived values (`bookingToken`, `cancellationtime`,\n`lcid`) ARE plain server-rendered text, so `fpx get` + `grep` gets them fine\n— see `references/easytable-requests.md` for the full payload shape and a\nmanual-token workaround (read the Turnstile input yourself from the browser\nconsole). Treat create/modify as a manual/hybrid flow, not a clean one-shot.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. Write endpoints answer HTTP 200 even on a *rejected*\n  booking (`Status:0` in the body) — always check the parsed `Status`, not\n  just the exit code.\n- `2` — bridge unavailable: extension not connected or pairing pending → run\n  `fpx pair -p easytable`, confirm an easytable tab is open.\n- `3` — bot wall: the tab hasn't cleared Cloudflare → open/refresh a\n  `book.easytable.com` tab and retry.\n- `4` — upstream non-2xx from easyTable.\n\n## Notes\n\n- No easyTable account or API key involved — this is the user's own action\n  on their own signed-in browser session.\n- `fpx health -p easytable` shows bridge connection state when a call fails.\n- Don't fire real create/cancel calls just to \"test\" the flow — a\n  `confirm`-equivalent action here books/cancels a real table at a real\n  restaurant.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.3:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"easytable-fpx\",\n  \"version\": \"1.1.3\",\n  \"publishedAt\": 1790199606693\n}\n\nFile v1.1.3:references/easytable-requests.md\n\n# easyTable requests for fpx\n\nReady-to-run URLs for `fpx get '<url>' -p easytable -H 'X-Requested-With: XMLHttpRequest'`\n(reads) and `fpx post-json`/`fpx get` (writes). All paths/params/field shapes\nare transcribed from `src/client.ts`, `src/parse.ts`, `src/payload.ts`, and\n`docs/EASYTABLE-API.md` — the same shapes the `easytable_*` MCP tools use.\n\nHost: `https://book.easytable.com`. Every URL below needs `id=<placeId>`\n(a.k.a. `place`) and `lang=<code>` (`en`, `se`, `da`, … — defaults to `en`).\nReplace `1fdfc` with a real restaurant id from a\n`book.easytable.com/book/?id=<id>` link.\n\nReads answer **HTML fragments**, not JSON — extraction below is `grep -oE`\nagainst the exact attributes `src/parse.ts` selects on. These are compact\ngreps, not a full parser; for anything beyond simple extraction, read\n`src/parse.ts`.\n\n---\n\n## 1. List booking areas/types (resolve this first)\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nThe numeric value after `type=` in the `href` is the `type` id every later\ncall needs.\n\n## 2. List bookable dates\n\nNeeds `type` (from §1) and `qty` (party size).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/calendar.asp?id=1fdfc&lang=en&type=13991&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*day[^\"]*\"[^>]*data-date=\"[0-9-]+\"[^>]*>'\n```\n\nA `class` containing `av` = available; `closed`/`ua` = not. `data-note`\n(when present) carries a note.\n\n## 3. List time slots\n\nNeeds `type`, a `date` (ISO `YYYY-MM-DD`, from §2), and `qty`. Slot values\nare **minute-of-day** (1035 → 17:15 — `hh=floor(m/60)`, `mm=m%60`).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/times.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*time[^\"]*\"[^>]*data-time=\"[0-9]+\"[^>]*>'\n```\n\n## 4. Find existing bookings by phone\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/cancel-search.asp?id=1fdfc&lang=en&mobile=%2B46701234567' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE 'data-booking=\"[^\"]+\"|data-id=\"[^\"]+\"|data-mobile=\"[^\"]+\"'\n```\n\nURL-encode the mobile (`+` → `%2B`). The booking id is on\n`data-booking`/`data-id` (`easytable-mcp` prefers `data-booking`, falling\nback to `data-id` — never `value`, which can be a button label).\n\n## 5. Cancel a booking (no Turnstile — plain tokenless GET)\n\n```sh\nfpx get 'https://book.easytable.com/user/ajax/json_cancel_booking.asp?place=1fdfc&mobile=%2B46701234567&booking=<bookingId>' \\\n  -p easytable\n```\n\nThe response is JSONP-wrapped (`callback([{Status,...}])`); unwrap and read\nwith:\n\n```sh\nsed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] | {Status, errHtml, successHtml, confirmUrl}'\n```\n\n`Status: 1` = cancelled; `Status: 0` = rejected (see `errHtml`).\n\n## 6. Create / modify a booking — payload shape (Turnstile caveat applies)\n\nHarvest the three plain-text page-derived values first — these ARE\nreachable via `fpx get` (they're server-rendered, unlike the Turnstile\ntoken):\n\n```sh\n# bookingToken + cancellationtime, from the confirm.asp fragment:\nfpx get 'https://book.easytable.com/book/ajax/confirm.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&time=1035&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' > /tmp/confirm.html\ngrep -oE 'bookingToken\\s*=\\s*\"[^\"]+\"' /tmp/confirm.html          # → {EB9351E5-...}\ngrep -oE 'id=\"cancellationtime\"[^>]*value=\"[0-9]+\"' /tmp/confirm.html  # → e.g. 180\n\n# lcid, from the widget page HTML (language-dependent: se→1053, en→2057):\nfpx get 'https://book.easytable.com/book/?id=1fdfc&lang=en' -p easytable \\\n  | grep -oE '\\blcid\\s*=\\s*\"?[0-9]+'\n```\n\nThe **Turnstile token cannot be harvested through `fpx`** — the CLI has no\nDOM-read verb. Manual workaround: with the widget tab open and loaded (past\nthe confirm step so Turnstile has rendered), open DevTools console on that\ntab and run:\n\n```js\ndocument.querySelector('input[name=\"cf-turnstile-response\"]').value\n```\n\nCopy that value in as `turnstileToken` below. It's single-use and expires in\n~300s, so harvest it right before the POST.\n\nPOST body — the full 32-key `dataObj` (the endpoint 500s on a partial\npayload; `time`/`persons` MUST be strings; omit `event`/`existing` when\nunused):\n\n```json\n{\n  \"place\": \"1fdfc\",\n  \"type\": \"13991\",\n  \"date\": \"2026-08-01\",\n  \"persons\": \"2\",\n  \"time\": \"1035\",\n  \"name\": \"Guest Name\",\n  \"mobile\": \"+46701234567\",\n  \"email\": \"\",\n  \"room\": \"\",\n  \"company\": \"\",\n  \"address\": \"\",\n  \"zip\": \"\",\n  \"city\": \"\",\n  \"country\": \"\",\n  \"comment\": \"\",\n  \"newsletter\": 0,\n  \"newsletterrelated\": 0,\n  \"lcid\": \"2057\",\n  \"lang\": \"en\",\n  \"ref\": \"\",\n  \"cancellationtime\": \"180\",\n  \"promocode\": \"\",\n  \"groupRequestTerms\": 0,\n  \"preorder\": {},\n  \"tags\": [],\n  \"amounttags\": [],\n  \"customFields\": [],\n  \"bookingInfo\": [],\n  \"bookingToken\": \"{EB9351E5-...}\",\n  \"botScore\": 0,\n  \"websitePot\": \"\",\n  \"turnstileToken\": \"<paste the harvested token>\"\n}\n```\n\n```sh\nfpx post-json 'https://book.easytable.com/user/ajax/json_booking.asp' @/tmp/booking.json -p easytable \\\n  | sed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] // .'\n```\n\nFor **modify**, POST the same shape plus `\"existing\": \"<existingBookingId>\"`\nto `https://book.easytable.com/user/ajax/json_modify_booking.asp`.\n\nA `Status: 0` response with the token/bookingToken/cancellationtime fields\nall present and non-empty means the request itself is malformed — re-check\nthe harvested values and the field types (`time`/`persons` as strings).\n\nFile v1.1.3:skill-card.md\n\n## Description:\n\nCheck restaurant availability and manage easyTable bookings from a shell with the fpx CLI, including listing booking areas, dates, and times, looking up bookings by phone, and cancelling bookings through a signed-in browser tab.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and operators use this skill to guide an agent through EasyTable availability checks and booking-management commands when they need browser-backed access from a shell instead of a running MCP server. It is intended for authorized reservation workflows where the restaurant, customer phone number, date, time, and booking id can be confirmed before any write action.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Live write commands can cancel, create, or modify real restaurant bookings.\n\nMitigation: Use write commands only for authorized reservations, and confirm the restaurant, customer, date, time, phone number, and booking id before submitting any cancel, create, or modify request.\n\nRisk: Booking lookups and reservation payloads can expose phone numbers and booking details.\n\nMitigation: Treat phone numbers and reservation details as personal data and avoid sharing command output beyond the authorized workflow.\n\nRisk: Testing live write endpoints can unintentionally alter real bookings.\n\nMitigation: Do not use live create, cancel, or modify calls merely to test the flow; prefer read-only availability checks unless a real authorized booking action is intended.\n\n## Reference(s):\n\n- [easyTable request recipes](references/easytable-requests.md)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/easytable-fpx)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, Configuration, API calls]\n\n**Output Format:** [Markdown with inline shell commands and request examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands may return HTML fragments or JSONP responses that require parsing before acting on results.]\n\n## Skill Version(s):\n\n1.1.3 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.2: 4 files, 6504 bytes\n\nFiles: references/easytable-requests.md (5701b), skill-card.md (2221b), SKILL.md (4701b), _meta.json (132b)\n\nFile v1.1.2:SKILL.md\n\n---\nname: easytable-fpx\ndescription: >-\n  Check restaurant availability and manage easyTable bookings\n  (book.easytable.com/book/?id=<placeId>) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the easytable-mcp server — list\n  booking areas/dates/times, look up a booking by phone, and cancel it, all\n  one-shot through a signed-in browser tab. Use when you want easyTable\n  access without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# easyTable via fpx (no MCP)\n\neasyTable fronts every `book.easytable.com` endpoint — reads and writes alike\n— with a Cloudflare check that 403s any plain `curl`/Node request. `fpx`\nroutes the request through the user's own signed-in browser tab (the\nTransporter extension), which has already cleared the challenge, so the same\nrequests succeed. There's no easyTable login: a restaurant is identified only\nby its `id` (a.k.a. `place`) from its widget URL\n`https://book.easytable.com/book/?id=<placeId>`.\n\nThis is the same data/actions the `easytable_*` MCP tools use, reached with\none-shot CLI calls instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli               # provides `fpx`\nfpx profile add easytable --domain easytable.com\nfpx pair -p easytable                        # prints a pair code → approve in Transporter\n```\n\nRequirements: the **Transporter** extension installed, with an open\n`https://book.easytable.com/book/?id=<placeId>` tab left to finish loading\n(its Turnstile check solves itself), and Chrome **Site access** allowing\n`easytable.com`. Pairing persists after the first approval.\n\n## Core call\n\nReads are GET requests that return **HTML fragments** (not JSON) — the\nwidget loads them into a container div. Send the `X-Requested-With` header\nthe widget sends, and pipe the fragment to `grep`/`sed` rather than `jq`:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest'\n```\n\nReady-to-run URLs + extraction recipes for every read, plus the cancel write\nand the create/modify payload shape, are in\n`references/easytable-requests.md`. Exhaustive parsing lives in the repo at\n`src/parse.ts` / `docs/EASYTABLE-API.md` — the recipes here are compact\ngreps against the same markup those parsers target.\n\n## The one rule: resolve the `type` first\n\nDates and times are scoped to a booking area. Always list types first, take\nthe `type` id, then feed it to dates/times:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nSame pattern for cancelling: resolve the booking id via `cancel-search.asp`\n(by mobile number) before calling the cancel endpoint.\n\n## Creating or modifying a booking — not fully doable through fpx\n\n`easytable_create_booking`/`easytable_modify_booking` need a live Cloudflare\n**Turnstile token**, read from the widget's hidden\n`input[name=\"cf-turnstile-response\"]`. The MCP harvests it with a bespoke\n`read_dom` verb wired into *this repo's own* fetchproxy transport\n(`bridge-fetchproxy.ts`) — the stock `fpx` CLI has no DOM-read command (only\n`get`/`post-json`/`request`/`cookies`/`local-storage`/`session-storage`/\n`indexeddb`/`session`), so it cannot harvest that token.\n\nThe other three page-derived values (`bookingToken`, `cancellationtime`,\n`lcid`) ARE plain server-rendered text, so `fpx get` + `grep` gets them fine\n— see `references/easytable-requests.md` for the full payload shape and a\nmanual-token workaround (read the Turnstile input yourself from the browser\nconsole). Treat create/modify as a manual/hybrid flow, not a clean one-shot.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. Write endpoints answer HTTP 200 even on a *rejected*\n  booking (`Status:0` in the body) — always check the parsed `Status`, not\n  just the exit code.\n- `2` — bridge unavailable: extension not connected or pairing pending → run\n  `fpx pair -p easytable`, confirm an easytable tab is open.\n- `3` — bot wall: the tab hasn't cleared Cloudflare → open/refresh a\n  `book.easytable.com` tab and retry.\n- `4` — upstream non-2xx from easyTable.\n\n## Notes\n\n- No easyTable account or API key involved — this is the user's own action\n  on their own signed-in browser session.\n- `fpx health -p easytable` shows bridge connection state when a call fails.\n- Don't fire real create/cancel calls just to \"test\" the flow — a\n  `confirm`-equivalent action here books/cancels a real table at a real\n  restaurant.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.2:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"easytable-fpx\",\n  \"version\": \"1.1.2\",\n  \"publishedAt\": 1790179299808\n}\n\nFile v1.1.2:references/easytable-requests.md\n\n# easyTable requests for fpx\n\nReady-to-run URLs for `fpx get '<url>' -p easytable -H 'X-Requested-With: XMLHttpRequest'`\n(reads) and `fpx post-json`/`fpx get` (writes). All paths/params/field shapes\nare transcribed from `src/client.ts`, `src/parse.ts`, `src/payload.ts`, and\n`docs/EASYTABLE-API.md` — the same shapes the `easytable_*` MCP tools use.\n\nHost: `https://book.easytable.com`. Every URL below needs `id=<placeId>`\n(a.k.a. `place`) and `lang=<code>` (`en`, `se`, `da`, … — defaults to `en`).\nReplace `1fdfc` with a real restaurant id from a\n`book.easytable.com/book/?id=<id>` link.\n\nReads answer **HTML fragments**, not JSON — extraction below is `grep -oE`\nagainst the exact attributes `src/parse.ts` selects on. These are compact\ngreps, not a full parser; for anything beyond simple extraction, read\n`src/parse.ts`.\n\n---\n\n## 1. List booking areas/types (resolve this first)\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nThe numeric value after `type=` in the `href` is the `type` id every later\ncall needs.\n\n## 2. List bookable dates\n\nNeeds `type` (from §1) and `qty` (party size).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/calendar.asp?id=1fdfc&lang=en&type=13991&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*day[^\"]*\"[^>]*data-date=\"[0-9-]+\"[^>]*>'\n```\n\nA `class` containing `av` = available; `closed`/`ua` = not. `data-note`\n(when present) carries a note.\n\n## 3. List time slots\n\nNeeds `type`, a `date` (ISO `YYYY-MM-DD`, from §2), and `qty`. Slot values\nare **minute-of-day** (1035 → 17:15 — `hh=floor(m/60)`, `mm=m%60`).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/times.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*time[^\"]*\"[^>]*data-time=\"[0-9]+\"[^>]*>'\n```\n\n## 4. Find existing bookings by phone\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/cancel-search.asp?id=1fdfc&lang=en&mobile=%2B46701234567' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE 'data-booking=\"[^\"]+\"|data-id=\"[^\"]+\"|data-mobile=\"[^\"]+\"'\n```\n\nURL-encode the mobile (`+` → `%2B`). The booking id is on\n`data-booking`/`data-id` (`easytable-mcp` prefers `data-booking`, falling\nback to `data-id` — never `value`, which can be a button label).\n\n## 5. Cancel a booking (no Turnstile — plain tokenless GET)\n\n```sh\nfpx get 'https://book.easytable.com/user/ajax/json_cancel_booking.asp?place=1fdfc&mobile=%2B46701234567&booking=<bookingId>' \\\n  -p easytable\n```\n\nThe response is JSONP-wrapped (`callback([{Status,...}])`); unwrap and read\nwith:\n\n```sh\nsed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] | {Status, errHtml, successHtml, confirmUrl}'\n```\n\n`Status: 1` = cancelled; `Status: 0` = rejected (see `errHtml`).\n\n## 6. Create / modify a booking — payload shape (Turnstile caveat applies)\n\nHarvest the three plain-text page-derived values first — these ARE\nreachable via `fpx get` (they're server-rendered, unlike the Turnstile\ntoken):\n\n```sh\n# bookingToken + cancellationtime, from the confirm.asp fragment:\nfpx get 'https://book.easytable.com/book/ajax/confirm.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&time=1035&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' > /tmp/confirm.html\ngrep -oE 'bookingToken\\s*=\\s*\"[^\"]+\"' /tmp/confirm.html          # → {EB9351E5-...}\ngrep -oE 'id=\"cancellationtime\"[^>]*value=\"[0-9]+\"' /tmp/confirm.html  # → e.g. 180\n\n# lcid, from the widget page HTML (language-dependent: se→1053, en→2057):\nfpx get 'https://book.easytable.com/book/?id=1fdfc&lang=en' -p easytable \\\n  | grep -oE '\\blcid\\s*=\\s*\"?[0-9]+'\n```\n\nThe **Turnstile token cannot be harvested through `fpx`** — the CLI has no\nDOM-read verb. Manual workaround: with the widget tab open and loaded (past\nthe confirm step so Turnstile has rendered), open DevTools console on that\ntab and run:\n\n```js\ndocument.querySelector('input[name=\"cf-turnstile-response\"]').value\n```\n\nCopy that value in as `turnstileToken` below. It's single-use and expires in\n~300s, so harvest it right before the POST.\n\nPOST body — the full 32-key `dataObj` (the endpoint 500s on a partial\npayload; `time`/`persons` MUST be strings; omit `event`/`existing` when\nunused):\n\n```json\n{\n  \"place\": \"1fdfc\",\n  \"type\": \"13991\",\n  \"date\": \"2026-08-01\",\n  \"persons\": \"2\",\n  \"time\": \"1035\",\n  \"name\": \"Guest Name\",\n  \"mobile\": \"+46701234567\",\n  \"email\": \"\",\n  \"room\": \"\",\n  \"company\": \"\",\n  \"address\": \"\",\n  \"zip\": \"\",\n  \"city\": \"\",\n  \"country\": \"\",\n  \"comment\": \"\",\n  \"newsletter\": 0,\n  \"newsletterrelated\": 0,\n  \"lcid\": \"2057\",\n  \"lang\": \"en\",\n  \"ref\": \"\",\n  \"cancellationtime\": \"180\",\n  \"promocode\": \"\",\n  \"groupRequestTerms\": 0,\n  \"preorder\": {},\n  \"tags\": [],\n  \"amounttags\": [],\n  \"customFields\": [],\n  \"bookingInfo\": [],\n  \"bookingToken\": \"{EB9351E5-...}\",\n  \"botScore\": 0,\n  \"websitePot\": \"\",\n  \"turnstileToken\": \"<paste the harvested token>\"\n}\n```\n\n```sh\nfpx post-json 'https://book.easytable.com/user/ajax/json_booking.asp' @/tmp/booking.json -p easytable \\\n  | sed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] // .'\n```\n\nFor **modify**, POST the same shape plus `\"existing\": \"<existingBookingId>\"`\nto `https://book.easytable.com/user/ajax/json_modify_booking.asp`.\n\nA `Status: 0` response with the token/bookingToken/cancellationtime fields\nall present and non-empty means the request itself is malformed — re-check\nthe harvested values and the field types (`time`/`persons` as strings).\n\nFile v1.1.2:skill-card.md\n\n## Description:\n\nCheck restaurant availability and manage easyTable bookings from a shell with the fpx CLI, including listing booking areas, dates, and times, looking up bookings by phone, and cancelling bookings through a paired browser tab.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and operators use this skill when they need command-line access to easyTable availability and booking-management workflows without running the easytable MCP server. It is intended for actions on the user's own browser-backed booking session.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Shell commands can change real easyTable reservations, including cancellation and hybrid create or modify flows.\n\nMitigation: Confirm the restaurant, phone number, booking ID, date, time, and party size before running any write command, and do not use write commands as tests.\n\nRisk: The workflow relies on a paired browser session and may expose or act on booking data visible to that session.\n\nMitigation: Use the skill only with the user's own booking session and only with consent for the booking data being queried or changed.\n\n## Reference(s):\n\n- [easyTable requests for fpx](references/easytable-requests.md)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/easytable-fpx)\n- [easyTable booking widget](https://book.easytable.com)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Configuration instructions, Guidance, Markdown]\n\n**Output Format:** [Markdown with inline shell commands and request examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Includes command examples for fpx GET and POST requests, HTML-fragment parsing guidance, and cautionary notes for real booking operations.]\n\n## Skill Version(s):\n\n1.1.2 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.1: 4 files, 6507 bytes\n\nFiles: references/easytable-requests.md (5701b), skill-card.md (2163b), SKILL.md (4701b), _meta.json (132b)\n\nFile v1.1.1:SKILL.md\n\n---\nname: easytable-fpx\ndescription: >-\n  Check restaurant availability and manage easyTable bookings\n  (book.easytable.com/book/?id=<placeId>) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the easytable-mcp server — list\n  booking areas/dates/times, look up a booking by phone, and cancel it, all\n  one-shot through a signed-in browser tab. Use when you want easyTable\n  access without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# easyTable via fpx (no MCP)\n\neasyTable fronts every `book.easytable.com` endpoint — reads and writes alike\n— with a Cloudflare check that 403s any plain `curl`/Node request. `fpx`\nroutes the request through the user's own signed-in browser tab (the\nTransporter extension), which has already cleared the challenge, so the same\nrequests succeed. There's no easyTable login: a restaurant is identified only\nby its `id` (a.k.a. `place`) from its widget URL\n`https://book.easytable.com/book/?id=<placeId>`.\n\nThis is the same data/actions the `easytable_*` MCP tools use, reached with\none-shot CLI calls instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli               # provides `fpx`\nfpx profile add easytable --domain easytable.com\nfpx pair -p easytable                        # prints a pair code → approve in Transporter\n```\n\nRequirements: the **Transporter** extension installed, with an open\n`https://book.easytable.com/book/?id=<placeId>` tab left to finish loading\n(its Turnstile check solves itself), and Chrome **Site access** allowing\n`easytable.com`. Pairing persists after the first approval.\n\n## Core call\n\nReads are GET requests that return **HTML fragments** (not JSON) — the\nwidget loads them into a container div. Send the `X-Requested-With` header\nthe widget sends, and pipe the fragment to `grep`/`sed` rather than `jq`:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest'\n```\n\nReady-to-run URLs + extraction recipes for every read, plus the cancel write\nand the create/modify payload shape, are in\n`references/easytable-requests.md`. Exhaustive parsing lives in the repo at\n`src/parse.ts` / `docs/EASYTABLE-API.md` — the recipes here are compact\ngreps against the same markup those parsers target.\n\n## The one rule: resolve the `type` first\n\nDates and times are scoped to a booking area. Always list types first, take\nthe `type` id, then feed it to dates/times:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nSame pattern for cancelling: resolve the booking id via `cancel-search.asp`\n(by mobile number) before calling the cancel endpoint.\n\n## Creating or modifying a booking — not fully doable through fpx\n\n`easytable_create_booking`/`easytable_modify_booking` need a live Cloudflare\n**Turnstile token**, read from the widget's hidden\n`input[name=\"cf-turnstile-response\"]`. The MCP harvests it with a bespoke\n`read_dom` verb wired into *this repo's own* fetchproxy transport\n(`bridge-fetchproxy.ts`) — the stock `fpx` CLI has no DOM-read command (only\n`get`/`post-json`/`request`/`cookies`/`local-storage`/`session-storage`/\n`indexeddb`/`session`), so it cannot harvest that token.\n\nThe other three page-derived values (`bookingToken`, `cancellationtime`,\n`lcid`) ARE plain server-rendered text, so `fpx get` + `grep` gets them fine\n— see `references/easytable-requests.md` for the full payload shape and a\nmanual-token workaround (read the Turnstile input yourself from the browser\nconsole). Treat create/modify as a manual/hybrid flow, not a clean one-shot.\n\n## Exit codes (fetch verbs)\n\n- `0` — success. Write endpoints answer HTTP 200 even on a *rejected*\n  booking (`Status:0` in the body) — always check the parsed `Status`, not\n  just the exit code.\n- `2` — bridge unavailable: extension not connected or pairing pending → run\n  `fpx pair -p easytable`, confirm an easytable tab is open.\n- `3` — bot wall: the tab hasn't cleared Cloudflare → open/refresh a\n  `book.easytable.com` tab and retry.\n- `4` — upstream non-2xx from easyTable.\n\n## Notes\n\n- No easyTable account or API key involved — this is the user's own action\n  on their own signed-in browser session.\n- `fpx health -p easytable` shows bridge connection state when a call fails.\n- Don't fire real create/cancel calls just to \"test\" the flow — a\n  `confirm`-equivalent action here books/cancels a real table at a real\n  restaurant.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.1:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"easytable-fpx\",\n  \"version\": \"1.1.1\",\n  \"publishedAt\": 1789964077459\n}\n\nFile v1.1.1:references/easytable-requests.md\n\n# easyTable requests for fpx\n\nReady-to-run URLs for `fpx get '<url>' -p easytable -H 'X-Requested-With: XMLHttpRequest'`\n(reads) and `fpx post-json`/`fpx get` (writes). All paths/params/field shapes\nare transcribed from `src/client.ts`, `src/parse.ts`, `src/payload.ts`, and\n`docs/EASYTABLE-API.md` — the same shapes the `easytable_*` MCP tools use.\n\nHost: `https://book.easytable.com`. Every URL below needs `id=<placeId>`\n(a.k.a. `place`) and `lang=<code>` (`en`, `se`, `da`, … — defaults to `en`).\nReplace `1fdfc` with a real restaurant id from a\n`book.easytable.com/book/?id=<id>` link.\n\nReads answer **HTML fragments**, not JSON — extraction below is `grep -oE`\nagainst the exact attributes `src/parse.ts` selects on. These are compact\ngreps, not a full parser; for anything beyond simple extraction, read\n`src/parse.ts`.\n\n---\n\n## 1. List booking areas/types (resolve this first)\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nThe numeric value after `type=` in the `href` is the `type` id every later\ncall needs.\n\n## 2. List bookable dates\n\nNeeds `type` (from §1) and `qty` (party size).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/calendar.asp?id=1fdfc&lang=en&type=13991&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*day[^\"]*\"[^>]*data-date=\"[0-9-]+\"[^>]*>'\n```\n\nA `class` containing `av` = available; `closed`/`ua` = not. `data-note`\n(when present) carries a note.\n\n## 3. List time slots\n\nNeeds `type`, a `date` (ISO `YYYY-MM-DD`, from §2), and `qty`. Slot values\nare **minute-of-day** (1035 → 17:15 — `hh=floor(m/60)`, `mm=m%60`).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/times.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*time[^\"]*\"[^>]*data-time=\"[0-9]+\"[^>]*>'\n```\n\n## 4. Find existing bookings by phone\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/cancel-search.asp?id=1fdfc&lang=en&mobile=%2B46701234567' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE 'data-booking=\"[^\"]+\"|data-id=\"[^\"]+\"|data-mobile=\"[^\"]+\"'\n```\n\nURL-encode the mobile (`+` → `%2B`). The booking id is on\n`data-booking`/`data-id` (`easytable-mcp` prefers `data-booking`, falling\nback to `data-id` — never `value`, which can be a button label).\n\n## 5. Cancel a booking (no Turnstile — plain tokenless GET)\n\n```sh\nfpx get 'https://book.easytable.com/user/ajax/json_cancel_booking.asp?place=1fdfc&mobile=%2B46701234567&booking=<bookingId>' \\\n  -p easytable\n```\n\nThe response is JSONP-wrapped (`callback([{Status,...}])`); unwrap and read\nwith:\n\n```sh\nsed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] | {Status, errHtml, successHtml, confirmUrl}'\n```\n\n`Status: 1` = cancelled; `Status: 0` = rejected (see `errHtml`).\n\n## 6. Create / modify a booking — payload shape (Turnstile caveat applies)\n\nHarvest the three plain-text page-derived values first — these ARE\nreachable via `fpx get` (they're server-rendered, unlike the Turnstile\ntoken):\n\n```sh\n# bookingToken + cancellationtime, from the confirm.asp fragment:\nfpx get 'https://book.easytable.com/book/ajax/confirm.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&time=1035&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' > /tmp/confirm.html\ngrep -oE 'bookingToken\\s*=\\s*\"[^\"]+\"' /tmp/confirm.html          # → {EB9351E5-...}\ngrep -oE 'id=\"cancellationtime\"[^>]*value=\"[0-9]+\"' /tmp/confirm.html  # → e.g. 180\n\n# lcid, from the widget page HTML (language-dependent: se→1053, en→2057):\nfpx get 'https://book.easytable.com/book/?id=1fdfc&lang=en' -p easytable \\\n  | grep -oE '\\blcid\\s*=\\s*\"?[0-9]+'\n```\n\nThe **Turnstile token cannot be harvested through `fpx`** — the CLI has no\nDOM-read verb. Manual workaround: with the widget tab open and loaded (past\nthe confirm step so Turnstile has rendered), open DevTools console on that\ntab and run:\n\n```js\ndocument.querySelector('input[name=\"cf-turnstile-response\"]').value\n```\n\nCopy that value in as `turnstileToken` below. It's single-use and expires in\n~300s, so harvest it right before the POST.\n\nPOST body — the full 32-key `dataObj` (the endpoint 500s on a partial\npayload; `time`/`persons` MUST be strings; omit `event`/`existing` when\nunused):\n\n```json\n{\n  \"place\": \"1fdfc\",\n  \"type\": \"13991\",\n  \"date\": \"2026-08-01\",\n  \"persons\": \"2\",\n  \"time\": \"1035\",\n  \"name\": \"Guest Name\",\n  \"mobile\": \"+46701234567\",\n  \"email\": \"\",\n  \"room\": \"\",\n  \"company\": \"\",\n  \"address\": \"\",\n  \"zip\": \"\",\n  \"city\": \"\",\n  \"country\": \"\",\n  \"comment\": \"\",\n  \"newsletter\": 0,\n  \"newsletterrelated\": 0,\n  \"lcid\": \"2057\",\n  \"lang\": \"en\",\n  \"ref\": \"\",\n  \"cancellationtime\": \"180\",\n  \"promocode\": \"\",\n  \"groupRequestTerms\": 0,\n  \"preorder\": {},\n  \"tags\": [],\n  \"amounttags\": [],\n  \"customFields\": [],\n  \"bookingInfo\": [],\n  \"bookingToken\": \"{EB9351E5-...}\",\n  \"botScore\": 0,\n  \"websitePot\": \"\",\n  \"turnstileToken\": \"<paste the harvested token>\"\n}\n```\n\n```sh\nfpx post-json 'https://book.easytable.com/user/ajax/json_booking.asp' @/tmp/booking.json -p easytable \\\n  | sed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] // .'\n```\n\nFor **modify**, POST the same shape plus `\"existing\": \"<existingBookingId>\"`\nto `https://book.easytable.com/user/ajax/json_modify_booking.asp`.\n\nA `Status: 0` response with the token/bookingToken/cancellationtime fields\nall present and non-empty means the request itself is malformed — re-check\nthe harvested values and the field types (`time`/`persons` as strings).\n\nFile v1.1.1:skill-card.md\n\n## Description:\n\nCheck restaurant availability and manage easyTable bookings from a shell with the fpx CLI instead of running the easytable-mcp server.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and operators use this skill to list easyTable booking areas, dates, and times, find bookings by phone, and prepare or run fpx commands for authorized booking management.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Cancel, create, or modify commands can change real restaurant reservations.\n\nMitigation: Use the skill only for bookings the user is authorized to manage, and require explicit final confirmation before any write command.\n\nRisk: The workflow can handle personal data such as names, phone numbers, and emails.\n\nMitigation: Limit data exposure to the minimum needed for the booking task and avoid retaining copied payloads or command outputs.\n\nRisk: Browser-extension pairing persists and can continue to route requests through the user's browser session.\n\nMitigation: Confirm the intended browser profile and easyTable tab before use, and disconnect or unpair the fpx profile when access is no longer needed.\n\n## Reference(s):\n\n- [easyTable requests for fpx](references/easytable-requests.md)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/easytable-fpx)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown with inline shell commands, endpoint URLs, and JSON payload examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Includes cautions for real booking changes, confirmation checks, browser pairing, and personal data handling.]\n\n## Skill Version(s):\n\n1.1.1 (source: ClawHub release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.","readmeExcerpt":"Skill: easytable-fpx Owner: chrischall Summary: Check restaurant availability and manage easyTable bookings (book.easytable.com/book/?id=<placeId>) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the easytable-mcp server — list booking areas/dates/times, look up a booking by phone, and cancel it, all one-shot through a signed-in browser tab. Use when you want easyTable access without the MCP, in a ","codeSnippets":[],"executableExamples":[{"language":"sh","snippet":"npm install -g @fetchproxy/cli               # provides `fpx`\nfpx profile add easytable --domain easytable.com\nfpx pair -p easytable                        # prints a pair code → approve in ContextMint Bridge"},{"language":"sh","snippet":"fpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest'"},{"language":"sh","snippet":"fpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>"},{"language":"sh","snippet":"fpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>"},{"language":"sh","snippet":"fpx get 'https://book.easytable.com/book/ajax/calendar.asp?id=1fdfc&lang=en&type=13991&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*day[^\"]*\"[^>]*data-date=\"[0-9-]+\"[^>]*>'"},{"language":"sh","snippet":"fpx get 'https://book.easytable.com/book/ajax/times.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*time[^\"]*\"[^>]*data-time=\"[0-9]+\"[^>]*>'"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: easytable-fpx\ndescription: >-\n  Check restaurant availability and manage easyTable bookings\n  (book.easytable.com/book/?id=<placeId>) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the easytable-mcp server — list\n  booking areas/dates/times, look up a booking by phone, and cancel it, all\n  one-shot through a signed-in browser tab. Use when you want easyTable\n  access without the MCP, in a script, or on a machine where the MCP isn't\n  installed.\n---\n\n# easyTable via fpx (no MCP)\n\neasyTable fronts every `book.easytable.com` endpoint — reads and writes alike\n— with a Cloudflare check that 403s any plain `curl`/Node request. `fpx`\nroutes the request through the user's own signed-in browser tab (the\nContextMint Bridge extension), which has already cleared the challenge, so the same\nrequests succeed. There's no easyTable login: a restaurant is identified only\nby its `id` (a.k.a. `place`) from its widget URL\n`https://book.easytable.com/book/?id=<placeId>`.\n\nThis is the same data/actions the `easytable_*` MCP tools use, reached with\none-shot CLI calls instead of a running server.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli               # provides `fpx`\nfpx profile add easytable --domain easytable.com\nfpx pair -p easytable                        # prints a pair code → approve in ContextMint Bridge\n```\n\nRequirements: the **ContextMint Bridge** extension installed (from\nhttps://github.com/nullnet-app/contextmint-bridge/releases — Chrome: load the\nchrome zip unpacked; Safari: ships inside the ContextMint app, no public download\nyet, so use Chrome for now — it is the renamed fetchproxy extension, same maintainer; source is public, so\nbuild it or verify the zip with its published `.sha256`), with an open\n`https://book.easytable.com/book/?id=<placeId>` tab left to finish loading\n(its Turnstile check solves itself), and Chrome **Site access** allowing\n`easytable.com`. Pairing persists after the first approval.\n\n## Core call\n\nReads are GET requests that return **HTML fragments** (not JSON) — the\nwidget loads them into a container div. Send the `X-Requested-With` header\nthe widget sends, and pipe the fragment to `grep`/`sed` rather than `jq`:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest'\n```\n\nReady-to-run URLs + extraction recipes for every read, plus the cancel write\nand the create/modify payload shape, are in\n`references/easytable-requests.md`. Exhaustive parsing lives in the repo at\n`src/parse.ts` / `docs/EASYTABLE-API.md` — the recipes here are compact\ngreps against the same markup those parsers target.\n\n## The one rule: resolve the `type` first\n\nDates and times are scoped to a booking area. Always list types first, take\nthe `type` id, then feed it to dates/times:\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=["},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"easytable-fpx\",\n  \"version\": \"1.2.6\",\n  \"publishedAt\": 1791588272307\n}"},{"path":"references/easytable-requests.md","content":"# easyTable requests for fpx\n\nReady-to-run URLs for `fpx get '<url>' -p easytable -H 'X-Requested-With: XMLHttpRequest'`\n(reads) and `fpx post-json`/`fpx get` (writes). All paths/params/field shapes\nare transcribed from `src/client.ts`, `src/parse.ts`, `src/payload.ts`, and\n`docs/EASYTABLE-API.md` — the same shapes the `easytable_*` MCP tools use.\n\nHost: `https://book.easytable.com`. Every URL below needs `id=<placeId>`\n(a.k.a. `place`) and `lang=<code>` (`en`, `se`, `da`, … — defaults to `en`).\nReplace `1fdfc` with a real restaurant id from a\n`book.easytable.com/book/?id=<id>` link.\n\nReads answer **HTML fragments**, not JSON — extraction below is `grep -oE`\nagainst the exact attributes `src/parse.ts` selects on. These are compact\ngreps, not a full parser; for anything beyond simple extraction, read\n`src/parse.ts`.\n\n---\n\n## 1. List booking areas/types (resolve this first)\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/types.asp?id=1fdfc&lang=en' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<a[^>]*type=[0-9]+[^>]*>[^<]*</a>'\n# <a href=\"#type=13991\">Boka Inne</a>\n```\n\nThe numeric value after `type=` in the `href` is the `type` id every later\ncall needs.\n\n## 2. List bookable dates\n\nNeeds `type` (from §1) and `qty` (party size).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/calendar.asp?id=1fdfc&lang=en&type=13991&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*day[^\"]*\"[^>]*data-date=\"[0-9-]+\"[^>]*>'\n```\n\nA `class` containing `av` = available; `closed`/`ua` = not. `data-note`\n(when present) carries a note.\n\n## 3. List time slots\n\nNeeds `type`, a `date` (ISO `YYYY-MM-DD`, from §2), and `qty`. Slot values\nare **minute-of-day** (1035 → 17:15 — `hh=floor(m/60)`, `mm=m%60`).\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/times.asp?id=1fdfc&lang=en&type=13991&date=2026-08-01&qty=2' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE '<span[^>]*class=\"[^\"]*time[^\"]*\"[^>]*data-time=\"[0-9]+\"[^>]*>'\n```\n\n## 4. Find existing bookings by phone\n\n```sh\nfpx get 'https://book.easytable.com/book/ajax/cancel-search.asp?id=1fdfc&lang=en&mobile=%2B46701234567' \\\n  -p easytable -H 'X-Requested-With: XMLHttpRequest' \\\n  | grep -oE 'data-booking=\"[^\"]+\"|data-id=\"[^\"]+\"|data-mobile=\"[^\"]+\"'\n```\n\nURL-encode the mobile (`+` → `%2B`). The booking id is on\n`data-booking`/`data-id` (`easytable-mcp` prefers `data-booking`, falling\nback to `data-id` — never `value`, which can be a button label).\n\n## 5. Cancel a booking (no Turnstile — plain tokenless GET)\n\n```sh\nfpx get 'https://book.easytable.com/user/ajax/json_cancel_booking.asp?place=1fdfc&mobile=%2B46701234567&booking=<bookingId>' \\\n  -p easytable\n```\n\nThe response is JSONP-wrapped (`callback([{Status,...}])`); unwrap and read\nwith:\n\n```sh\nsed -E 's/^[^(]*\\(//; s/\\);?[[:space:]]*$//' | jq '.[0] | {Status, errHtml, successHtml, confirmUrl}'\n```\n\n`Status: 1` = cancelled; `Status: 0` = rejected (see `errHtml`).\n\n## "},{"path":"skill-card.md","content":"## Description:\n\nGuides agents through checking easyTable restaurant availability and managing reservations with shell commands routed through a paired browser tab.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents can check restaurant booking areas, dates, and times, find reservations by phone, and cancel authorized bookings from a shell. Creating or modifying a booking requires a manual browser step.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Browser-session access to protected easyTable endpoints may expose reservation information.\n\nMitigation: Review the external CLI and browser extension before pairing, and use only a browser session you are authorized to operate.\n\nRisk: Cancellation, creation, or modification can change a real restaurant booking.\n\nMitigation: Confirm authorization and booking details before any write; do not send live writes merely to test the flow.\n\n## Reference(s):\n\n- [easyTable request recipes](references/easytable-requests.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n- [easytable-fpx release](https://clawhub.ai/chrischall/skills/easytable-fpx)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Guidance, Configuration instructions]\n\n**Output Format:** [Markdown with shell commands and example booking payloads]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Create and modify flows require a manual browser step; booking writes can affect real reservations.]\n\n## Skill Version(s):\n\n1.2.6 (source: server-resolved release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":1392,"uniquenessScore":44,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T11:55:10.704Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T11:55:10.704Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T14:46:13.737Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}