@reopt-ai/data-cli
v0.1.0
Published
reopt-data command line: provision organizations and projects, run analytics queries, and upload source maps — built for CI and AI agents
Maintainers
Readme
@reopt-ai/data-cli
reopt-data — the command line for a reopt-data deployment. Provision organizations and projects, run analytics queries, manage the event catalogue, and upload source maps from CI. Built to be driven by scripts and AI agents as much as by people.
npm install -g @reopt-ai/data-cli # or: npx @reopt-ai/data-cli --help
reopt-data --helpNode 22 or newer. Runtime dependencies: @reopt-ai/data-contract, zod, and @modelcontextprotocol/server (loaded only by reopt-data mcp).
Commands
| Group | Command | Needs | Effect |
| ----------- | -------------------------------------------------------------------------- | -------------------------- | ------------------- |
| org | create, update | platform key | writes |
| org | rotate-key | platform key | destructive --yes |
| project | list, get | org key | read-only |
| project | create, update | org key | writes |
| project | delete, rotate-secret | org key | destructive --yes |
| event | set-meta | org key | writes |
| query | timeseries, funnel, retention, traffic-overview, traffic-sources | client credential | read-only |
| sourcemap | inject, upload | key by target | writes, --dry-run |
| event | init, pull, diff, verify, push, types, history | org key (init/types: none) | see below |
| config | link (also reopt-data link), show | none | local writes |
reopt-data <group> <command> --help prints every flag with its type, default and whether it is required — generated from the same schema the parser validates against.
Credentials
Set them in the environment. Flags (--platform-key, --org-key, --client-id, --client-secret) exist for one-off use; they land in shell history.
| Variable | Issued by | Used by |
| --------------------------------------------------- | ------------------------------------------------- | ---------------------------------------------- |
| REOPT_DATA_API_URL | you; default https://data.reopt.ai | everything |
| REOPT_DATA_PLATFORM_KEY | the deployment operator | org *, platform uploads |
| REOPT_DATA_ORG_KEY | org create (once) / org rotate-key | project *, event *, project & host uploads |
| REOPT_DATA_CLIENT_ID + REOPT_DATA_CLIENT_SECRET | project create (once) / project rotate-secret | query * |
A command that lacks its credential exits 2 before making any request and names the variable to set.
Output contract
stdout carries the result and nothing else. Default
--format jsonprints the server's response verbatim — the shape documented in@reopt-ai/data-contract.--format textgives a one-line human summary where one exists.stderr carries progress (
--quietsilences it) and, on failure, one envelope:{ "error": "auth_error", "message": "…", "exitCode": 2, "status": 401, "apiCode": "invalid_key", "requestId": "…" }Exit codes name the caller's next move:
| Code | Name | Do | | ---- | ----------------------- | ----------------------------------------------------------- | | 0 |
success| | | 1 |api_error| server or network failed;retryAfterMsif the server said | | 2 |auth_error| set or rotate the credential named inmessage| | 3 |validation_error| fix the input;details.issueslists each field | | 4 |config_error| environment incomplete (URL, directory, peer dependency) | | 5 |internal_error| CLI bug — please report withrequestIdif present | | 6 |partial_failure| some items failed; the result on stdout lists them | | 7 |confirmation_required| destructive command; re-run with--yes| | 8 |drift_detected|event verify: file and server disagree; pull or push |
Giving input
Three equivalent ways, merged (later wins):
# flags, kebab-case, one per field
reopt-data query funnel --project-id prj_1 --start-date 2026-08-01 --end-date 2026-08-31 \
--steps view_item --steps add_to_cart --steps purchase
# one JSON object — inline, from a file, or from stdin
reopt-data query funnel --input '{"projectId":"prj_1","steps":["view_item","purchase"],…}'
reopt-data query funnel --input @funnel.json
echo '{…}' | reopt-data query funnel --input @-
# positionals for ids
reopt-data project get prj_1Arrays of scalars repeat the flag; nested objects take JSON (--property-filters '[{"key":"plan","value":"pro"}]'). null clears a nullable field (--symbol-host-app null). Booleans stand alone (--conversion, --no-conversion).
For agents
reopt-data tools --jsonreturns every command with its inputSchema (JSON Schema 2020-12), flags, which credential it needs, whether it mutates, whether it is destructive, supportsDryRun, and ready-to-run examples. Read it once; the schemas are the contract package's own, so what validates here validates at the server.
Rules an agent can rely on:
- A destructive command never runs without
--yes— it exits 7 first, before any network call. - Validation and credential checks happen before any request; exit 2 and 3 mean nothing was sent.
--dry-runonsourcemap uploadandsourcemap injecttouches nothing and needs no key. On any other command it is refused (exit 3) rather than ignored.- Creating with the same
--external-idtwice is safe: the second call answerscreated:falseand returns no secret. - stdout is always parseable when the exit code is 0 or 6.
MCP
reopt-data mcpserves the same tools over the Model Context Protocol on stdio — one entry per command, named reopt_data_<group>_<command> (reopt_data_project_create, reopt_data_query_funnel, …). Credentials come from the environment the client starts the server with. Destructive tools take a required confirmed: true argument instead of --yes; dry-runnable ones take dryRun. Annotations (readOnlyHint, destructiveHint) are derived from the same definitions, and a failed call returns the CLI's error envelope as its text with isError: true.
{
"mcpServers": {
"reopt-data": {
"command": "npx",
"args": ["-y", "@reopt-ai/data-cli", "mcp"],
"env": { "REOPT_DATA_ORG_KEY": "…" }
}
}
}The same registry is importable for in-process use:
import { allTools, run, toolCatalog } from "@reopt-ai/data-cli";Event catalogue as code
Declare the project's event catalogue in reopt-data.events.json next to the code that emits the events, and let CI keep the server in step with the commit that ships:
{
"$schema": "https://data.reopt.ai/schemas/event-catalog.v1.json",
"version": 1,
"projectId": "prj_123",
"events": {
"purchase": { "displayName": "Purchase", "conversion": true, "rollupProperties": ["plan", "currency"] },
"healthcheck": { "status": "internal" },
"$pageview": {},
},
}The file is the truth: a field you leave out is the default, and an event you remove is archived (never deleted). A lock file beside it (reopt-data.events.lock.json, commit it) records the last sync so diff can tell your edits from console edits.
reopt-data event init --project-id prj_123 # or: event pull, to start from what the server has
reopt-data event diff --format text # + create ~ update - archive ! changed on the server
reopt-data event push # plan only
reopt-data event push --apply --yes # write; --yes covers metric-changing and expensive changes
reopt-data event verify # CI gate: exit 8 on drift
reopt-data event types --out src/reopt-events.d.ts # ReoptEventName / ReoptEventProperties for a typed track()Changes are graded: safe (display fields), metric-changing (conversion, status) and expensive (rollupProperties, which queues a rollup rebuild); the last two need --yes. A push sends the last sync's updatedAt with every event, so a console edit made in between is refused as a whole (409) instead of half-applied — event pull first, or --force to overwrite.
Source maps in CI
# after `next build`
reopt-data sourcemap inject --dir .next/static
reopt-data sourcemap upload --dir .next/static \
--url-prefix https://shop.example.com/_next/static --project-id "$REOPT_DATA_PROJECT_ID" \
--release "$GIT_SHA" --delete-after-uploadOne bundle serving many projects uploads once per host app, keyed by path:
reopt-data sourcemap upload --dir .next/static --path-prefix /_next/static \
--host-app studio-web --organization-id org_abc --release "$GIT_SHA"inject-chunk-ids and upload-sourcemaps — the spellings from @reopt-ai/data-sdk-server's bin — still work, as do its --api-key (either upload key) and --silent (= --quiet) flags. Note the exit code for a partial failure is 6, where the old bin used 1.
License
MIT
