otick
v0.1.0
Published
OpenTicket CLI — discover and buy event tickets from the terminal over the public agent-commerce surface (REST feed + MCP).
Maintainers
Readme
otick
Buy event tickets from the terminal, over OpenTicket's public agent-commerce
surface — the plain REST discovery feed (GET /api/events) plus the MCP server
(buy_ticket / get_order over JSON-RPC). No privileged endpoints: the CLI is
dogfooding of the same contract an autonomous agent gets.
Status: feature-complete v1 (cards A2–A7). Not yet published to npm (A8) — install from source.
Install (from source, until A8)
git clone <this repo> && cd otick
npm install
npm run build # esbuild → dist/bin.js (self-contained, shebang)
npm link # exposes `otick` on PATH (or: node dist/bin.js …)Requires Node >= 20 (native fetch, stable util.parseArgs).
Configure
By default the CLI talks to http://localhost:3000 (a local open-ticket dev
server). Override per invocation with env vars:
export OPENTICKET_BASE_URL=https://your-openticket.example
export OPENTICKET_API_KEY=ot_live_... # optional; `login` persists it insteadCommands
otick events # upcoming events + ticket types (table)
otick search jazz # filter by title/venue/description
otick search jazz --json | jq # machine-readable
otick login <key> # store your API key (chmod 0600)
echo "$KEY" | otick login # keep the key out of shell history
otick whoami # show local config (base URL, key last4)
otick buy <tt_id> --limit 50USD --email [email protected]
otick buy <tt_id> --limit 50USD --email [email protected] --wait # poll + write .ics
otick watch # live ticker of sales (SSE), Ctrl-C to stop
otick watch --json | jq # one JSON object per event (NDJSON)buy resolves the event from the ticket type (get_ticket), validates your
input client-side against the same Zod schema the server uses, auto-generates
an idempotency key, calls buy_ticket and prints the Stripe checkout_url.
--wait polls get_order until confirmed, then downloads the .ics from the
order's ics_path and writes otick-<order>.ics to the current directory.
watch streams the public GET /api/ticker SSE feed (the same one the landing
page uses), parsing the data: frames by hand — no eventsource dependency. It
reconnects with backoff across the serverless function recycle and exits 0 on
Ctrl-C. --json emits NDJSON (one event per line).
--json works on every command; human text goes to stdout, diagnostics to
stderr, so pipes stay clean.
Exit codes (stable, ≥2 anchored to AgentCommerceErrorCode)
| Code | Meaning |
|---|---|
| 0 | ok (including duplicate — the previous order is returned) |
| 1 | generic / usage / network / internal / not_implemented |
| 2 | sold_out |
| 3 | mandate_exceeded |
| 4 | invalid_intent (client- or server-side validation) |
| 5 | event_unavailable |
| 6 | payment_failed |
| 7 | unauthorized / missing API key |
| 8 | rate_limited |
| 10 | --wait timed out before confirmed |
Agents branch on these; they will not change without a major version bump.
whoami is local-only in v1
There is no public endpoint to probe key validity (design decision, not an
oversight). whoami reports the local config only. A server-side whoami MCP
tool is a possible future card.
Design & spec of record
- Design:
open-ticket/docs/design-cli.md(card A1). - Vendored contract:
src/vendor/core/mirrorsopen-ticket/core/byte-for-byte (seesrc/vendor/core/PROVENANCE.md). A drift test guards it.
Development
npm run dev -- search jazz # run TS directly (node --experimental-strip-types)
npm run typecheck # tsc --noEmit
npm test # vitest: parsing, dispatch, drift, (e2e opt-in)Tests
- parsing —
--limitmoney parsing + exit-code mapping. Pure, offline. - dispatch — network-free command routing (help/version/usage/errors).
- watch — the SSE parser end-to-end against a throwaway loopback server
(NDJSON output, heartbeat skipping, a frame split across chunks, clean stop).
Offline; runs in plain
npm test. - drift — vendored core vs
../open-ticket/core(byte-identical). Skips if the sibling checkout is absent; setOPENTICKET_REPO=/path/to/open-ticketto point elsewhere. - e2e — opt-in, needs a live server. Covers
events/search,whoamiwith a configured key, andbuyreaching a mapped error (exit 7) — none of which need Stripe:HARNESS_URL=http://localhost:3000 [OTICK_E2E_KEY=<key> OTICK_E2E_TT=<tt_id>] npm test. The fullbuy --wait → confirmed → .icsleg is a separate opt-in gated behindOTICK_E2E_STRIPE=1(needs Stripe test-mode confirmation).
A cli:otick check is also wired into the open-ticket harness
(scripts/harness.sh): when this CLI is built at ../otick, the harness runs
otick events --json against its live server and asserts exit 0 + an events
array; otherwise it reports the check as BLOCKED (never fails).
Not in this build
- npm publish, release workflow, name reservation — card A8 (needs Lucas' OK).
