@@ -5,9 +5,9 @@ authentication and identity only — `auth login` (OAuth or API key), `auth
55logout` , ` auth status` , and ` auth whoami`. Resource commands (contacts,
66sequences, inbox, …) come later.
77
8- The CLI is one binary, ` reply ` . It targets ** prod** by default; other backends
9- are opt-in via user-defined profiles (see [ Profiles ] ( #profiles ) ). Credentials
10- live in a per-user config dir with strict permissions.
8+ One binary, ` reply ` . It talks to Reply.io ** prod** out of the box — no setup,
9+ no flags. (Profiles, below, are an optional extra for people who need more than
10+ one account.)
1111
1212## Install
1313
@@ -21,19 +21,21 @@ npm install -g @reply-team/reply-cli@next # newest build off main (for teste
2121npm install -g @reply-team/reply-cli@0.1.42 # a specific build
2222```
2323
24- Every commit on ` main ` is published under the ` next ` tag, so testers can install
25- ** any ** build. Released builds are promoted to ` latest ` .
24+ Every commit on ` main ` is published under ` next ` ; released builds are promoted
25+ to ` latest ` .
2626
2727## Authentication
2828
29+ For most people this is the whole story — no profiles, no config:
30+
2931``` bash
3032# OAuth (authorization code + PKCE, loopback redirect — opens your browser)
3133reply auth login
3234
3335# API key from stdin (kept out of shell history / argv)
3436echo " $MY_API_KEY " | reply auth login --with-token
3537
36- # Active credential source, profile, user, and OAuth expiry (no secrets)
38+ # Active credential source, user, and OAuth expiry (no secrets)
3739reply auth status
3840
3941# Validate the active credential against the API and print the principal
@@ -58,59 +60,79 @@ Resolved strictly in this order — first hit wins:
5860The flag and env var are ** ephemeral** — used for the current invocation only,
5961never written to disk. There is no ` .env ` file lookup.
6062
61- ## Profiles
63+ ## Output & exit codes
64+
65+ - Data goes to ** stdout** ; status and error messages go to ** stderr** .
66+ - ` --json ` emits compact JSON; ` --pretty ` emits indented JSON.
67+ - On ` --json ` /` --pretty ` , an error is a single machine-readable line:
68+ ` {"error":{"status":…,"code":…,"title":…,"detail":…,"hint":…}} ` .
69+
70+ Exit codes: ` 0 ` success · ` 1 ` API/runtime failure · ` 2 ` usage error.
6271
63- There is no built-in environment enum. A ** profile** is a named backend bundle
64- you define yourself. Which profile a command uses is resolved in this order:
72+ ## Multiple accounts (optional — profiles)
73+
74+ Skip this section unless you juggle more than one Reply.io account. A ** profile**
75+ is a named credential slot; each keeps its own login, so you can switch between
76+ accounts without re-authenticating. Most people name profiles after the account
77+ email.
78+
79+ ``` bash
80+ reply profile add alice@reply.io # create (no URLs needed — inherits prod)
81+ reply profile add bob@reply.io
82+ reply profile use alice@reply.io # make it current (persisted until changed)
83+ reply auth login # logs in as alice; stored under alice@reply.io
84+
85+ reply profile use bob@reply.io && reply auth login # separate login for bob
86+
87+ reply profile list # * marks the current profile
88+ reply profile current # print the current profile name
89+ reply --profile bob@reply.io auth whoami # one-off override, ignores current
90+ ```
91+
92+ Which profile a command uses is resolved as:
6593
6694```
6795--profile <name> > REPLY_PROFILE > current profile > default (prod)
6896```
6997
70- Define your own profiles in the config file:
98+ Credentials are keyed by profile name, so accounts never collide even though
99+ they all hit the same prod backend.
71100
72- ``` jsonc
73- // ~/.config/reply/config.json (Windows: %APPDATA%\reply\config.json)
74- {
75- " profiles" : {
76- " dev" : {
77- " authority" : " https://oauth.dev.replyapp.io" ,
78- " api_base" : " https://api.dev.reply.io/v3"
79- }
80- }
81- }
82- ```
101+ ### Advanced: a non-prod backend
83102
84- Use a profile per-command, or set a ** current** profile so you don't repeat
85- ` --profile ` :
103+ A profile can also point at a different backend by overriding the URLs; any
104+ field you omit is inherited from the embedded default (prod). This is mainly for
105+ internal testing against dev:
86106
87107``` bash
88- reply --profile dev auth whoami # one-off override
89-
90- reply profile use dev # make dev current (persisted until changed)
91- reply profile list # list profiles; * marks the current one
92- reply profile current # print the current profile name
93- reply auth whoami # now uses dev, no flag needed
108+ reply profile add dev \
109+ --authority https://oauth.dev.replyapp.io \
110+ --api-base https://api.dev.reply.io/v3
111+ reply --profile dev auth login
94112```
95113
96- Each profile resolves to its own backend host, and credentials are stored
97- per-host — so a ` dev ` session and your default (prod) session never collide.
98- An unknown profile is a usage error.
114+ Or hand-edit the config file:
115+
116+ ``` jsonc
117+ // ~/.config/reply/config.json (Windows: %APPDATA%\reply\config.json)
118+ { " profiles" : { " dev" : { " authority" : " https://…" , " api_base" : " https://…/v3" } } }
119+ ```
99120
100121## Credential storage
101122
102123Credentials live in a JSON file in the per-user config dir, created with strict
103- permissions (` 0600 ` file inside a ` 0700 ` dir) — the same plaintext-file model as
104- ` gh ` , ` aws ` , and ` az ` :
124+ permissions (` 0600 ` file in a ` 0700 ` dir) — the gh/aws/az plaintext-file model:
105125
106- - Linux/macOS: ` ~/.config/reply/hosts.json ` (honors ` $XDG_CONFIG_HOME ` )
107- - Windows: ` %APPDATA%\reply\hosts.json `
126+ - Linux/macOS: ` ~/.config/reply/credentials.json ` (honors ` $XDG_CONFIG_HOME ` )
127+ - Windows: ` %APPDATA%\reply\credentials.json `
128+ (on Windows the strict perms are a no-op — it relies on your per-user
129+ ` %APPDATA% ` ACLs, same as gh/aws/az)
108130
109- Each record is keyed by authority host and is either an OAuth record
110- (access + refresh token + expiry) or an API-key record. An expired OAuth access
111- token is refreshed automatically; if the refresh fails the record is cleared and
112- you are asked to log in again. The store sits behind a ` CredentialStore `
113- interface — an OS-keychain backend is a planned opt-in addition.
131+ Records are keyed by profile name; each is either an OAuth record (access +
132+ refresh token + expiry) or an API-key record. Expired OAuth tokens refresh
133+ automatically; if refresh fails the record is cleared and you're asked to log in
134+ again. The store sits behind a ` CredentialStore ` interface — an OS-keychain
135+ backend is a planned opt-in addition.
114136
115137## Configuration (env vars)
116138
@@ -120,15 +142,6 @@ interface — an OS-keychain backend is a planned opt-in addition.
120142| ` REPLY_PROFILE ` | Profile to use (same as ` --profile ` ) |
121143| ` REPLY_CONFIG_DIR ` | Override the per-user config directory |
122144
123- ## Output & exit codes
124-
125- - Data goes to ** stdout** ; status and error messages go to ** stderr** .
126- - ` --json ` emits compact JSON; ` --pretty ` emits indented JSON.
127- - On ` --json ` /` --pretty ` , an error is a single machine-readable line:
128- ` {"error":{"status":…,"code":…,"title":…,"detail":…,"hint":…}} ` .
129-
130- Exit codes: ` 0 ` success · ` 1 ` API/runtime failure · ` 2 ` usage error.
131-
132145## Development
133146
134147``` bash
@@ -140,7 +153,8 @@ npm link # puts the real `reply` binary on your PATH to drive by h
140153
141154Tests never contact the network: ` fetch ` is stubbed and the OAuth loopback is
142155exercised against a local ` 127.0.0.1 ` listener with an injected fake browser.
156+ CI runs the build + tests on Linux and Windows.
143157
144- ** Releasing:** the ` build ` workflow publishes every ` main ` build to ` @next ` .
145- When a build has been tested, the ` release ` workflow promotes that exact version
146- to ` @latest ` (a dist-tag move — no rebuild) and tags the source commit.
158+ ** Releasing:** the ` build ` workflow publishes every ` main ` build to ` @next ` ;
159+ the ` release ` workflow promotes a tested version to ` @latest ` (a dist-tag move —
160+ no rebuild) and tags the source commit.
0 commit comments