npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@odeva/cli

v0.0.12

Published

Build apps on the Odeva booking platform — scaffold, develop, deploy.

Readme

@odeva/cli

Build apps on the Odeva booking platform — scaffold, develop, deploy.

The odeva CLI is the developer toolkit for the Odeva booking platform. It is to Odeva what shopify is to Shopify and wrangler is to Cloudflare: scaffold a project, run it locally with a public tunnel and live webhook delivery, then ship it.

Install

npm install -g @odeva/cli
# or
bun add -g @odeva/cli

You'll also need cloudflared on your PATH for the dev tunnel:

# macOS
brew install cloudflared

# Debian/Ubuntu
sudo apt install cloudflared

# Other platforms: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/

Quickstart

odeva auth login          # paste a personal access token
odeva app init my-app     # scaffold a new app (default template: hono-bun)
cd my-app
bun install
odeva app config link     # register the app on Odeva, write client_id to toml
odeva app dev             # local server + tunnel + webhook auto-registration

In another terminal:

odeva webhook trigger reservation.created

You should see the fixture payload arrive in your dev server's logs, signed and ready to handle.

How odeva app dev works

              ┌─────────────────────────────────────────────┐
              │  Odeva platform                             │
              │   (creates webhook subscriptions, signs     │
              │    payloads, delivers events)               │
              └────────────────────┬────────────────────────┘
                                   │  POST https://<id>.trycloudflare.com/webhooks/...
                                   ▼
                ┌──────────────────────────────────┐
                │  cloudflared quick tunnel        │
                │  (started by `odeva app dev`)    │
                └────────────────┬─────────────────┘
                                 │  http://localhost:3000
                                 ▼
                ┌──────────────────────────────────┐
                │  Your Hono/Bun/Express app       │
                │  Verify signature with the       │
                │  ODEVA_WEBHOOK_SECRET injected   │
                │  into the dev process.           │
                └──────────────────────────────────┘

On Ctrl-C, the CLI tears down the tunnel and deletes the webhook subscriptions it created, so your production endpoints aren't polluted with dev URLs.

Commands

| Command | What it does | | --- | --- | | odeva auth login | Authenticate the CLI with a personal access token | | odeva auth logout | Remove stored credentials from this machine | | odeva auth whoami | Show the currently authenticated session | | odeva app init [name] | Scaffold a new app from a template (default: hono-bun) | | odeva app config link | Register the local app on Odeva, write client_id back to toml | | odeva app dev | Run the app locally with a public tunnel + webhook auto-registration | | odeva webhook list | List active subscriptions on the current org (--available shows event types) | | odeva webhook trigger <event> | Fire a signed sample payload at your local handler |

Run odeva help <command> for full flags on any command.

Config: odeva.app.toml

Each Odeva app has an odeva.app.toml at its root. Example:

name = "Cabin Manager"
slug = "cabin-manager"
client_id = "app_..."        # written by `odeva app config link`
description = "A booking add-on for Foo Holiday Park."

[build]
dev = "bun run dev"
port = 3000

[access_scopes]
scopes = ["reservations:read", "reservations:write"]

# Omit this section if the app doesn't surface inside the merchant admin.
[admin]
entry_url = "https://app.example.com/admin"

[admin.sidebar]
label = "Cabin Manager"
icon = "puzzle"

[webhooks]
api_version = "2026-01"

[[webhooks.subscriptions]]
topic = "reservation.created"
uri = "/webhooks/reservation.created"

[[webhooks.subscriptions]]
topic = "payment.succeeded"
uri = "/webhooks/payment.succeeded"

This file is the source of truth for app config. odeva app config link pushes it to the platform; odeva app dev reads webhook subscriptions from it to wire up the tunnel.

Embedding apps in the merchant admin

The admin embed surface lets your app appear as a sidebar entry inside the Odeva merchant admin, mounting your UI in an iframe. It is opt-in: the feature activates only when you declare an [admin] block in odeva.app.toml (see the Config example above) and run odeva app config link.

How it works

  1. Developer declares [admin] in odeva.app.toml (see the Config example above) and runs odeva app config link.
  2. Merchant installs the app from the marketplace.
  3. Merchant clicks the sidebar entry. odeva-admin mints a 5-min EdDSA session token via the mintAppSessionToken GraphQL mutation, then loads the iframe at entry_url?session_token=<jwt>&host=<base_url>.
  4. The app verifies the token against the platform JWKS at /.well-known/odeva/apps/jwks.json, checking iss and aud.
  5. Before the token expires, the app's backend calls POST /api/apps/session-token/refresh with X-Api-Key: <api_key> and hands the new token to the iframe.

Session token claims

Tokens are standard JWTs signed with EdDSA; the JOSE header carries alg, typ, and kid.

| Claim | Value | | --- | --- | | iss | Platform base URL (matches your ODEVA_API_URL) | | aud | The app's client_id (matches your ODEVA_APP_CLIENT_ID) | | sub | The app installation ID (string) | | org_id | The merchant organization ID (string) | | app_id | The app ID (string) | | exp | 5 minutes after iat | | iat | Issued-at (unix seconds) | | jti | Unique token nonce |

Verifying the token

The JWKS endpoint is ${ODEVA_API_URL}/.well-known/odeva/apps/jwks.json. The scaffolded template (odeva app init) provides a reference implementation in src/admin.ts; it uses the jose library with createRemoteJWKSet and jwtVerify.

Key points when verifying:

  • Check iss matches your ODEVA_API_URL.
  • Check aud matches your ODEVA_APP_CLIENT_ID.
  • Never accept alg: "none"jose's createRemoteJWKSet enforces this automatically; if you roll your own verifier you must enforce it yourself.

Refreshing the token

Tokens have a 5-minute TTL. Long-lived iframe sessions must refresh the token before it expires.

Refresh from the app's backend only — the api_key must not be exposed to the browser.

curl -X POST "$ODEVA_API_URL/api/apps/session-token/refresh" \
  -H "X-Api-Key: $ODEVA_API_KEY"
# -> { "token": "...", "expires_at": "2026-..." }

Success response: { token, expires_at } where expires_at is an ISO8601 timestamp.

Error codes:

| Status | Meaning | | --- | --- | | 401 | Missing, invalid, or revoked api_key | | 403 | api_key is not an app-installation key (e.g. a merchant key) | | 409 | Installation is not active, or the app has no [admin] declared | | 503 | Platform signing key not configured (operator-side; retry) |

Typical pattern: the backend refreshes ~30 s before expires_at and pushes the new token to the iframe via your own channel (postMessage, polling, or whatever fits the app).

Quick reference

  • JWKS: ${ODEVA_API_URL}/.well-known/odeva/apps/jwks.json
  • Refresh: ${ODEVA_API_URL}/api/apps/session-token/refresh
  • Reference implementation: src/admin.ts (scaffolded by odeva app init)
  • Env vars: ODEVA_API_URL, ODEVA_APP_CLIENT_ID, ODEVA_API_KEY
  • Not yet shipped: host↔app postMessage bridge, URL sync between iframe and host.

Templates

Bundled templates live under templates/ in this repo:

  • hono-bun (default) — Hono on Bun, runtime-agnostic. Deploys later to Node, Bun, Cloudflare Workers, or Deno without rewrites.

More templates (Next.js, Express, Fresh, WordPress) are on the roadmap — drop a directory into templates/ and it's available via odeva app init --template <name>.

Authentication

For now, the CLI authenticates with a personal access token (PAT). Generate one from the Odeva admin panel and paste it into odeva auth login. The token is stored at ~/.config/odeva/credentials.json with mode 0600.

Browser-based OAuth (device-code flow) lands once the developer dashboard ships.

Status

Pre-1.0. The command surface and odeva.app.toml schema are stabilizing. Expect breaking changes between minor versions until 1.0.

Development

git clone https://codeberg.org/odeva/odeva-cli
cd odeva-cli
bun install
bun run test
bun run typecheck
bun run build
node ./bin/run.js --help

License

MIT — see LICENSE.