Archived
2
0
Fork
You've already forked cli
0
No description
This repository has been archived on 2026年06月24日. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
  • TypeScript 99%
  • Go Template 0.8%
  • JavaScript 0.2%
Find a file
Renovate Bot 7fccf6b861
All checks were successful
CI / ci (push) Successful in 31s
chore(deps): update node.js to v26.3.1 ( #8 )
2026年06月24日 02:02:01 +02:00
.forgejo/workflows fix(deps): pin dependencies ( #7 ) 2026年06月17日 17:28:22 +02:00
bin Initial commit 2026年06月04日 08:50:48 +02:00
src fix: sigterm during probing would keep process 2026年06月07日 00:21:36 +02:00
templates/hono-bun refactor: simplify tunnel flow, so refreshing url is simpler 2026年06月06日 23:21:36 +02:00
test fix: sigterm during probing would keep process 2026年06月07日 00:21:36 +02:00
.gitignore Initial commit 2026年06月04日 08:50:48 +02:00
bun.lock fix(deps): pin dependencies ( #7 ) 2026年06月17日 17:28:22 +02:00
CHANGELOG.md Initial commit 2026年06月04日 08:50:48 +02:00
lefthook.yml Initial commit 2026年06月04日 08:50:48 +02:00
LICENSE Initial commit 2026年06月04日 08:50:48 +02:00
package.json chore(deps): update node.js to v26.3.1 ( #8 ) 2026年06月24日 02:02:01 +02:00
README.md feat: setup a more mature app flow 2026年06月05日 22:53:28 +02:00
tsconfig.json Initial commit 2026年06月04日 08:50:48 +02:00
tsup.config.ts Initial commit 2026年06月04日 08:50:48 +02:00

@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.