CLI Onboarding Guide
Credential-free local onboarding and inspection with the wats CLI.
active · reviewed 2026-05-25
The wats CLI is the package-manager entry point for local onboarding:
config/env scaffolding, a setup wizard, offline diagnostics, OpenAPI export,
and a local serve wrapper. It is credential-safe by default — no live Meta
calls, no token validation, no implicit env-file reads, no file overwrites.
Current commands
wats --help
wats --version
wats init --help
wats setup --help
wats setup ./my-bot --profile test
wats config validate <path>
wats config validate --config <path>
wats doctor --config <path>
wats doctor --config <path> --profile <name> --check-env
wats doctor --config <path> --format json
wats doctor --help
wats upgrade --dry-run
wats upgrade
wats update --dry-run
wats openapi --config <path>
wats openapi --config <path> --profile <name>
wats openapi --config <path> --server-url https://service.example
wats openapi --config <path> --out openapi.json
wats openapi --help
wats serve --config <path> --dry-run
wats serve --config <path> --dry-run --print-routes
wats serve --help
wats messages list --config <path> --env-file .env.local
wats messages show <message-id> --config <path> --env-file .env.local
wats onboarding --public-url <https URL>
wats onboarding --public-url <https URL> --webhook-path /webhooks/whatsapp
wats webhook token
wats webhook token --helpThe CLI does not:
- read or resolve live credentials from existing env files implicitly
- call Meta Graph APIs
- validate tokens against Meta
- manage multiple credential profiles interactively
- overwrite output files
First-run flow
wats init writes wats.config.yaml and .env.example only — it does not
create a package.json or tsconfig.json. From an empty directory, bootstrap
a TypeScript project and add the WATS runtime packages first, then run the
CLI onboarding commands:
mkdir ./my-bot && cd ./my-bot
bun init -y
bun add @wats/core @wats/graph @wats/http
bun add @wats/config @wats/service
wats init --dry-run
wats init --format yaml --profile local
wats config validate --config wats.config.yaml
wats --version
wats doctor --config wats.config.yaml --check-env
wats serve --config wats.config.yaml --dry-run- Create a project directory and a TypeScript baseline with
bun init -y. - Add the WATS runtime packages (
@wats/core,@wats/graph,@wats/http,@wats/config,@wats/service). - Preview generated files with
wats init --dry-run. - Generate config/env placeholder files with
wats init. - Validate the generated config through
@wats/config. - Run doctor offline diagnostics.
- Start a local dry-run service wrapper around
@wats/service.
There is no wats init --yes flag. Non-interactive setup passes the target
directory, format, and profile explicitly as shown above. wats setup is an
interactive wizard that needs a terminal (TTY stdin); in non-interactive
shells use wats init and edit .env.local instead.
Env placeholder policy
wats init generates env-secret references in config, not raw secrets:
auth:
accessToken:
env: WATS_ACCESS_TOKEN
webhook:
verifyToken:
env: WATS_VERIFY_TOKEN
appSecret:
env: WATS_APP_SECRET
service:
bearerToken:
env: WATS_SERVICE_TOKENChecked-in examples live at examples/config/wats.config.example.yaml,
examples/config/wats.config.example.json, and .env.example. They contain
placeholder env names only and both config examples parse through
@wats/config.
wats init --dry-run
wats init ./my-bot --format yaml --profile local
wats init ./my-bot --format=json --profile prodwats init writes wats.config.yaml or wats.config.json plus
.env.example, refuses to overwrite either file, prints only a redacted count
summary, and keeps .env.example secret-bearing values blank. Copy
.env.example to an ignored local file such as .env.local before filling
real values, or use wats setup for a guided local-only file write.
wats setup [dir] [--profile <name>] prompts for one profile's Graph
defaults, WABA id, phone-number id, access token, app secret, webhook path,
and local service defaults. Secret prompts display an Input hidden hint
before reading, so pasted tokens and app secrets do not echo. It writes
wats.config.yaml with env-secret references and .env.local with local
values, validates the generated config, refuses to overwrite either target,
and rolls back the config if .env.local cannot be created. Blank
verify/service-token answers generate local random wats_wh_... and
wats_srv_... values. Success output is only:
setup complete
files: 2
profile: [REDACTED_PROFILE]The setup wizard does not read existing .env.local, resolve env-secret
values, validate tokens against Meta, call Meta Graph APIs, manage multiple
profiles, or start the service. Prompt answers are validated for
empty/whitespace/control-character values, numeric bounds, safe path shape,
and safe profile identifiers before any file is written.
Do not pass raw secrets as CLI arguments. Do not commit access tokens, app secrets, webhook verify tokens, service bearer tokens, or WABA/phone-number ids from real accounts.
Validate a config file
wats config validate wats.config.json
# or
wats config validate --config wats.config.yamlValidation uses @wats/config. On success it prints only a safe count summary:
config valid
default profile: [REDACTED_PROFILE]
profiles: 1The summary intentionally does not print profile names, env-secret reference
names, or secret values. On failure, the command exits 1 and prints a compact
ConfigValidationError code/path/message without stack traces or
attacker-supplied file-path echoes.
Doctor offline diagnostics
wats doctor --config wats.config.yaml --profile local
wats doctor --config wats.config.yaml --profile local --check-env
wats doctor --config wats.config.yaml --format jsonwats doctor runs offline diagnostics for local operator readiness:
- runtime compatibility
- package imports
- package-version drift from
package.jsonfor public@wats/*dependencies - config discovery and validation
- selected profile existence
- route collision checks
- local OpenAPI generation
- env variable presence, only with
--check-env
The text output is status-only and redacted:
doctor ok
runtime: ok
package-imports: ok
packages: ok
config: ok
profile: ok
routes: ok
openapi: ok
summary: ok=7 warning=0 error=0--format json returns { ok, summary, checks } with stable check names. The
packages check reads package.json only and warns when a listed WATS
dependency appears older than the installed CLI; it does not call npm.
--check-env reports counts only (missing 1 required env value), never env
names or values. Doctor never calls Meta Graph APIs and never writes files.
Check and upgrade WATS package versions
wats --version
wats upgrade --dry-run
wats upgrade
# alias
wats updatewats --version prints the installed @wats/cli version. wats upgrade runs
Bun's package updater for the public WATS runtime package set:
bun update --latest @wats/cli @wats/core @wats/graph @wats/http @wats/config @wats/serviceUse --dry-run first to see the command without touching package.json or
bun.lock. The command reads only the current project's package.json before
running Bun and does not read .env.local or call Meta Graph APIs.
Export service OpenAPI
wats openapi --config wats.config.jsonThis prints OpenAPI 3.1 JSON for the WATS standalone service API implemented
by @wats/service.
wats openapi --config wats.config.json --profile prod
wats openapi --config wats.config.json --server-url https://service.example
wats openapi --config wats.config.json --out openapi.json--out writes only when explicit and refuses to overwrite existing files. Use
stdout redirection if you want shell-managed overwrite semantics.
The exported document is not a Meta Graph API OpenAPI document. It describes
only WATS service routes: health/readiness, configured webhook ingress,
message service routes, and /openapi.json.
Local verify-token generation
wats webhook tokenPrints a single random token prefixed with wats_wh_. Copy it into an
environment file manually if needed:
WATS_VERIFY_TOKEN=<generated-token>Do not commit the generated token.
Serve local flow
Dry-run mode is the safest first check:
wats serve --config wats.config.yaml --profile local --dry-run
wats serve --config wats.config.yaml --dry-run --print-routesDry-run loads config, starts the @wats/service process wrapper with
synthetic in-memory secrets, exposes health/readiness/OpenAPI/webhook routes,
and makes no live Meta calls.
For live webhook testing against Meta — public HTTPS tunnel, --live --yes-live --env-file, and the webhook onboarding checklist — see
Live webhook.
Troubleshooting
| Symptom | Likely cause | Next step |
|---|---|---|
config_not_found | no config discovered | pass --config wats.config.yaml or run wats init --dry-run |
profile_not_found | selected profile missing | check --profile, WATS_PROFILE, and defaultProfile |
missing_secret_env | live mode requested without a required env value | keep secrets in .env.local or the process environment and pass --env-file .env.local explicitly |
output_exists | generated file already exists | inspect the file; the CLI never overwrites |
port_in_use | service bind port already taken | choose another --port or stop the existing process |
live_confirmation_required | live check/service requested without acknowledgement | rerun only after reviewing side effects and adding --yes-live |
Safety defaults
- no raw secrets in command arguments
- no live Meta calls without explicit opt-in
- no secret values printed
- no env-secret reference names printed in validation or OpenAPI output
- no output-file overwrites
- generated files prefer env references over embedded secrets