@datarails-ai/dr-cli 0.0.1-locked → 1.6.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +608 -0
  2. package/dist/chunk-DWMCBGQH.js +242 -0
  3. package/dist/cli.js +64468 -0
  4. package/dist/kb-6UWI7Q32.js +21 -0
  5. package/dist/scopes-map.json +4412 -0
  6. package/dist/skills/connector-sandbox/SKILL.md +95 -0
  7. package/dist/skills/datarails-connect-workato/SKILL.md +140 -0
  8. package/dist/skills/datarails-connectors-onboard-connector/SKILL.md +813 -0
  9. package/dist/skills/dr-cli/SKILL.md +659 -0
  10. package/dist/skills/dr-cli/reference/admin.md +796 -0
  11. package/dist/skills/dr-cli/reference/budget-builder.md +151 -0
  12. package/dist/skills/dr-cli/reference/calc-vals.md +73 -0
  13. package/dist/skills/dr-cli/reference/calcmodels.md +76 -0
  14. package/dist/skills/dr-cli/reference/cash.md +61 -0
  15. package/dist/skills/dr-cli/reference/dashboards-widgets.md +85 -0
  16. package/dist/skills/dr-cli/reference/datamappers.md +150 -0
  17. package/dist/skills/dr-cli/reference/desk.md +472 -0
  18. package/dist/skills/dr-cli/reference/filebox-documents.md +373 -0
  19. package/dist/skills/dr-cli/reference/forecast.md +51 -0
  20. package/dist/skills/dr-cli/reference/integrations.md +57 -0
  21. package/dist/skills/dr-cli/reference/lookups.md +168 -0
  22. package/dist/skills/dr-cli/reference/metrics.md +512 -0
  23. package/dist/skills/dr-cli/reference/multi-product.md +180 -0
  24. package/dist/skills/dr-cli/reference/reconciliation-mec.md +76 -0
  25. package/dist/skills/dr-cli/reference/rh-config.md +84 -0
  26. package/dist/skills/dr-cli/reference/safety.md +143 -0
  27. package/dist/skills/dr-cli/reference/spend-control.md +206 -0
  28. package/dist/skills/dr-cli/reference/templates.md +163 -0
  29. package/dist/skills/dr-cli/reference/workflows.md +137 -0
  30. package/dist/skills/dr-error-enrichment/SKILL.md +80 -0
  31. package/dist/skills/dr-error-enrichment/reference/core.md +257 -0
  32. package/dist/skills/dr-error-enrichment/reference/groundcover.md +84 -0
  33. package/dist/skills/dr-error-enrichment/reference/jira-bugs.md +219 -0
  34. package/dist/skills/dr-error-enrichment/reference/safety-backup.md +122 -0
  35. package/dist/skills/dr-error-enrichment-author/SKILL.md +219 -0
  36. package/dist/skills/dr-error-enrichment-improve/SKILL.md +218 -0
  37. package/dist/skills/dr-error-enrichment-maintain/SKILL.md +237 -0
  38. package/dist/skills/dr-error-enrichment-reconcile/SKILL.md +167 -0
  39. package/dist/skills/dr-filebox-api-setup/SKILL.md +170 -0
  40. package/dist/skills/dr-switch-user/SKILL.md +395 -0
  41. package/dist/skills/dr-validate/SKILL.md +392 -0
  42. package/dist/skills/dr-validate/report-template.html +950 -0
  43. package/dist/skills/fx-conversion-matrix/SKILL.md +763 -0
  44. package/dist/skills/fx-conversion-matrix/scripts/_args.mjs +52 -0
  45. package/dist/skills/fx-conversion-matrix/scripts/_xlsx.mjs +241 -0
  46. package/dist/skills/fx-conversion-matrix/scripts/build-matrix.mjs +366 -0
  47. package/dist/skills/fx-conversion-matrix/scripts/gen-mappers.mjs +171 -0
  48. package/dist/skills/fx-conversion-matrix/scripts/rates-to-xlsx.mjs +172 -0
  49. package/dist/skills/fx-onboard/SKILL.md +325 -0
  50. package/dist/skills/mec-table-validation/SKILL.md +383 -0
  51. package/dist/skills/plugins.json +24 -0
  52. package/dist/skills/validate-workato-migration/SKILL.md +167 -0
  53. package/dist/skills/validate-workato-migration/reference/gotchas.md +16 -0
  54. package/dist/skills/validate-workato-migration/reference/staging-orgs.md +26 -0
  55. package/dist/skills/validate-workato-migration/scripts/compare_files.py +255 -0
  56. package/dist/zendeskKb-JPSHVOPH.js +243 -0
  57. package/package.json +56 -9
  58. package/scripts/postinstall.mjs +91 -0
package/README.md ADDED
@@ -0,0 +1,608 @@
1
+ # dr-cli
2
+
3
+ Datarails CLI. Server keys: `US`, `US2`, `UK`, `CA`, `EU`, `DEV`, `DEV-1`,
4
+ `TEST` — or pass a full `https://<host>.datarails.com` URL to reach an
5
+ ad-hoc (e.g. PR-preview) environment. Credentials live in the OS keychain (Windows Credential Manager,
6
+ macOS Keychain), or in a credential file when one is present — which wins over
7
+ the keychain, and is how `dr` runs where no keychain exists (CI, headless hosts,
8
+ containers). Binary is `dr`. Node.js 18+ / TypeScript / ESM.
9
+
10
+ A scriptable, diffable client for a customer's Datarails tenant — talks to the
11
+ same APIs the web UI does. Most endpoints go directly against the
12
+ `datarails-server` monolith; a few paths (e.g. `templates sample` /
13
+ `templates distinct` / `templates aggregate`) route through the `finance-os`
14
+ gateway at `/finance-os/api/data_models/v1/raw/…` because the monolith's
15
+ public table API ignores limit/offset. Both accept the same user JWT, so
16
+ there's nothing extra to configure.
17
+
18
+ ## What it does
19
+
20
+ - **Inspect a customer's environment** — tables, dashboards, functions,
21
+ dynamic ranges, datamappers, storyboards, reports, data flow, lookups,
22
+ semantic tables, extables.
23
+ - **Sample + aggregate rows** from any table via the finance-os gateway
24
+ (`templates sample`, `templates distinct`, `templates aggregate`).
25
+ - **Trigger** rescans, clone reports, prepare dashboards / storyboards.
26
+ - **IT triage** — users, organizations, devices, request/receive/download
27
+ device log bundles, feature flags, FX rates, metrics.
28
+ - **Search the customer Help Center** — `dr kb search`, `dr kb get`,
29
+ `dr kb article-images`, etc. Bridges product vocabulary
30
+ ("reconciliation isn't pairing", "how does FX translation work")
31
+ to dr-cli verbs.
32
+
33
+ Every top-level is a resource; subcommands are verbs. `--json` works
34
+ everywhere; `--server <KEY>` is required on anything that hits an API
35
+ (except `dr kb`, which is global Datarails content). A PR-preview or other ad-hoc environment is reachable by passing its full
36
+ URL instead of a key, e.g. `dr whoami --server https://<preview-host>.datarails.com`
37
+ (https + a `*.datarails.com` host only). Run
38
+ `dr <command> --help` for the current flag list — the binary is the source
39
+ of truth.
40
+
41
+ ## MCP integration with Claude Code
42
+
43
+ `dr-cli` ships an embedded MCP server (`dr-zendesk-kb`) that exposes
44
+ the Help Center search/browse surface to Claude Code as native
45
+ tools — Claude reaches for `search_kb` automatically when a request is
46
+ phrased in product terms, no need to type a shell command first.
47
+ `fetch_article_media` returns inline screenshots as image content
48
+ blocks so the LLM can see what the product UI looks like.
49
+
50
+ Auto-wired on every `npm install -g @datarails-ai/dr-cli` (the postinstall hook
51
+ adds an entry to `~/.claude.json`). Manage with:
52
+
53
+ ```
54
+ dr mcp list # show registered/unregistered status
55
+ dr mcp register dr-zendesk-kb # add (idempotent; postinstall does this)
56
+ dr mcp unregister dr-zendesk-kb # remove (postinstall re-adds on upgrade)
57
+ ```
58
+
59
+ Other LLM assistants (Cursor, Codex, Gemini CLI) get the same
60
+ capabilities via the shell route: `dr kb search "<query>"`,
61
+ `dr kb get <id>`, etc. — same surface, different transport.
62
+
63
+ ## Install
64
+
65
+ > **End users:** the polished install landing page is at
66
+ > [`docs/install.html`](docs/install.html) (hosted at
67
+ > <https://drcli.datarails.com/cli/install.html>). It walks through
68
+ > Claude Code install, Node, dr-cli, and per-session Claude usage with
69
+ > copy-to-clipboard buttons. The instructions below are the same flow,
70
+ > just terser.
71
+
72
+ Requires **Node.js 18+** (npm ships with Node — no extra tools needed).
73
+ The package is published to the Datarails JFrog registry; no auth setup
74
+ required.
75
+
76
+ ```
77
+ npm install -g @datarails-ai/dr-cli --registry https://datarails.jfrog.io/artifactory/api/npm/dr-cli-virtual
78
+ ```
79
+
80
+ That puts `dr` on PATH globally. To update later, re-run the same
81
+ command — it will pick up the latest version.
82
+
83
+ If you'd prefer not to type the registry every time, set it once:
84
+
85
+ ```
86
+ npm config set registry https://datarails.jfrog.io/artifactory/api/npm/dr-cli-virtual
87
+ npm install -g @datarails-ai/dr-cli
88
+ npm update -g @datarails-ai/dr-cli # later, to update
89
+ ```
90
+
91
+ (That sets your _default_ registry. To scope it only to the next
92
+ install, drop `npm config set` and pass `--registry` inline as above.)
93
+
94
+ ### One-shot installer scripts
95
+
96
+ For non-developer machines, two scripts at the repo root do the whole
97
+ thing — including installing Node 18+ if it's missing:
98
+
99
+ - **Windows (PowerShell):** `install.ps1`
100
+ - **macOS / Linux (bash):** `install.sh`
101
+
102
+ Both are also published at the fixed public URL, so users can run them
103
+ without cloning anything:
104
+
105
+ ```
106
+ # Windows
107
+ powershell -ExecutionPolicy Bypass -Command "irm https://drcli.datarails.com/cli/install.ps1 | iex"
108
+
109
+ # macOS / Linux
110
+ curl -fsSL https://drcli.datarails.com/cli/install.sh | bash
111
+ ```
112
+
113
+ Each script checks Node, installs it via the platform's package manager
114
+ if needed, then runs the `npm install -g` line above. Re-run any time to
115
+ update.
116
+
117
+ ### Build from source (developers only)
118
+
119
+ ```
120
+ git clone https://github.com/Datarails/dr-cli.git
121
+ cd dr-cli
122
+ node scripts/install.mjs
123
+ ```
124
+
125
+ The bootstrap installer runs `pnpm install`, `pnpm build`, `pnpm link
126
+ --global` (makes `dr` resolve to the workspace), and installs the embedded
127
+ skills into `~/.claude/skills/` unless the dr-cli plugin manages them. Requires
128
+ Node 18+ and pnpm.
129
+
130
+ If `dr: command not found` after `pnpm link --global`, run `pnpm setup`
131
+ once (adds pnpm's global bin dir to your shell profile), open a new
132
+ terminal, and `dr` will be there.
133
+
134
+ ## Run it without installing globally
135
+
136
+ From inside the repo, two equivalents exist:
137
+
138
+ ```
139
+ pnpm dev login --server US # runs src/cli.ts via tsx (no build needed)
140
+ node dist/cli.js login --server US # runs the built bundle (needs `pnpm build` first)
141
+ ```
142
+
143
+ ## First-time setup
144
+
145
+ ```
146
+ dr login --server US
147
+ dr whoami --server US # sanity-check identity
148
+ ```
149
+
150
+ Browser opens, you complete the OAuth device-code flow, and credentials
151
+ are stored in the **OS keychain** — Windows Credential Manager on
152
+ Windows, Keychain on macOS. Each session is keyed by
153
+ `(server, email)` so you can log in to multiple servers concurrently,
154
+ and to multiple Datarails accounts on the same server.
155
+
156
+ Multi-identity per server: if more than one account is stored for a
157
+ server, every command needs `--account <email>` to disambiguate. With a
158
+ single stored identity, `--account` can be omitted. The error message
159
+ when ambiguity is detected lists the stored emails.
160
+
161
+ JWT refresh is automatic. Re-run `login` only when the refresh token itself
162
+ dies (days of idle, password change, admin revoke). Never paste the
163
+ session/JWT — treat them like a password.
164
+
165
+ **Trying the v2 device flow (`DR_OAUTH_FLOW=v2`).** Set `DR_OAUTH_FLOW=v2` for
166
+ `dr login` to use the monolith's `/oauth/v2/` device flow. The consent page lets
167
+ you pick the credential's scope (below) before "Allow", and the login stores a
168
+ cookie-less JWT pair, with no session, refreshed at the external refresh
169
+ endpoint. The page offers the choice only where the org enforces it; elsewhere
170
+ the token is unrestricted, as today. Unset, or `legacy`, keeps the current flow;
171
+ any other value is refused. The server must be running the v2 endpoints
172
+ (DR-53508). Log in again to switch an identity between flows. Not yet
173
+ supported with a credential file (`DR_CREDENTIALS_FILE`): log in through the
174
+ keychain.
175
+
176
+ ```
177
+ DR_OAUTH_FLOW=v2 dr login --server US # PowerShell: $env:DR_OAUTH_FLOW="v2"; dr login --server US
178
+ ```
179
+
180
+ ### Credential scope
181
+
182
+ A credential can carry a **scope** — a `resource:action` ceiling set by whoever
183
+ approved it (an agent definition, an API key, a device-flow login), on top of
184
+ the user's own permissions rather than instead of them. Commands outside the
185
+ ceiling are hidden from `dr --help`, and running one anyway — or being refused
186
+ by a server that enforces the same claim — exits **3** naming the
187
+ `resource:action` that is missing, with `40301` meaning the credential's scope
188
+ does not cover the call and `40302` meaning that endpoint is not scope-declared
189
+ on the server yet (report those to #dr-cli; neither is fixed by logging in
190
+ again). **Run `dr whoami` to see the ceiling** — it prints the preset name
191
+ (`Read only`, `Read and write`, `Full access`), or a tree of the products,
192
+ resources and actions a custom scope allows, or
193
+ `unrestricted (no ceiling on this credential)` when the token carries no such
194
+ claim; `dr whoami --json` adds the claim exactly as the token carries it, under
195
+ `scopes` — an array, or the space-separated string an OAuth-shaped token uses,
196
+ or `null` when there is none.
197
+
198
+ ### Credential file (instead of the keychain)
199
+
200
+ Where the OS keychain cannot be used — CI runners, headless Linux,
201
+ containers — `dr` reads credentials from a file instead. The file **wins
202
+ over the keychain**: if it is there, every command uses it, even on a host
203
+ whose keychain works. Only when the path below is absent does `dr` touch
204
+ the keychain.
205
+
206
+ ```
207
+ ~/.dr/session.json # default location
208
+ DR_CREDENTIALS_FILE=<path> # override
209
+ ```
210
+
211
+ The file holds one identity:
212
+
213
+ ```json
214
+ { "access": "<jwt>", "refresh": "<jwt>", "server": "DEV", "email": "you@datarails.com" }
215
+ ```
216
+
217
+ It may instead hold a Datarails API key. `dr` exchanges it for a short-lived
218
+ access JWT on demand, so such a file authenticates every command without ever
219
+ holding a session cookie — the shape a CI provisioner writes. A key carries no
220
+ refresh token, so the exchange runs again when the JWT goes stale; a refusal
221
+ never deletes the stored key. `dr login` refuses the file — run
222
+ `dr login --api-key` to rotate the key, and update `apiKeyExpiresAt` alongside
223
+ it if the file carries one, since nothing derives that date — and `dr logout`
224
+ removes it. The refusal stands even if the file also carries a session cookie,
225
+ and applies to a keychain identity holding a key just the same: the key is what
226
+ every later command authenticates as, so a browser login over it would succeed
227
+ under one identity and run as another.
228
+
229
+ ```json
230
+ { "apiKey": "<key>", "server": "DEV", "email": "svc@datarails.com" }
231
+ ```
232
+
233
+ `email` names the stored identity — what `--account` selects and what the
234
+ keychain entry is keyed by. It need not match the principal the key is bound to;
235
+ the token the exchange mints is what every request authenticates as, and
236
+ `dr whoami` reports that token's own claims.
237
+
238
+ `accessApiKeyFingerprint` is written by `dr`, not by you: it records which key
239
+ minted the `access` beside it, so replacing `apiKey` is picked up on the very
240
+ next command rather than after the stale token expires. Leave it alone when you
241
+ rotate — a fingerprint naming the old key is exactly what triggers the exchange.
242
+
243
+ `apiKeyExpiresAt` is optional and read strictly: `YYYY-MM-DD`, or a timestamp
244
+ carrying an explicit offset (`2026-12-01T09:00:00Z`, `…+02:00`). An
245
+ offset-less timestamp would mean a different day on a laptop than on a CI
246
+ runner, so it is rejected — and a rejected value is simply never read, so the
247
+ warning silently stops rather than erroring.
248
+
249
+ `dr login` works normally against a file store — the device flow writes
250
+ the session into it, and `--server` defaults to the file's own server. It
251
+ re-authenticates the identity the file names; logging in as a _different_
252
+ account fails rather than silently writing nothing, since one file holds
253
+ one identity. `dr logout` removes the file (`--server` and `--all` both
254
+ mean that one identity, and neither flag is required). `dr login-as` is
255
+ refused while a file is in use, for the same one-identity reason — it has
256
+ no second slot to store the impersonated session in. `dr whoami` prints an
257
+ `Auth:` line naming the file in use.
258
+
259
+ Credentials provisioned by something _other_ than `dr login` carry no
260
+ session cookie. For a bare token pair, `dr` refuses `login` and `logout`,
261
+ since whatever provisioned it owns renewing it; remove the file to log in
262
+ interactively instead. An API-key file is different: see below.
263
+
264
+ > The file is written `0600` and holds live tokens — treat it like an SSH
265
+ > private key. Note it is **not** `~/.dr/credentials.json`; that path is a
266
+ > legacy pre-keychain store which `dr` deletes on sight.
267
+
268
+ > **Upgrading from a pre-keychain build?** On the first run after
269
+ > upgrade, dr-cli detects any legacy `~/.dr/credentials.json` and
270
+ > deletes it. The plaintext tokens are removed; your existing sessions
271
+ > are NOT migrated. Run `dr login --server <key>` once per server.
272
+
273
+ > **Tip — using "Login As" (JWT).** Log in once as the **operator/support
274
+ > user** with `dr login --server US` (browser device flow). Then impersonate
275
+ > any customer account without another browser round-trip:
276
+ > `dr login-as <email> --server US`. It resolves the email to a user id via
277
+ > `dr users can-login-as`, mints an impersonation JWT from the monolith
278
+ > (`POST /jwt/api/loginas/`), and stores it as a switchable identity keyed by
279
+ > `(server, email)` — so later commands target it with
280
+ > `--account <email>`. The impersonated token refreshes silently, and a dead
281
+ > refresh auto-remints from the operator, so you're never re-prompted until
282
+ > the operator's own login expires. Use `--as <operator-email>` to pick the
283
+ > operator when several are stored, or `--user-id <n>` to skip the lookup.
284
+
285
+ ### API keys (headless and CI)
286
+
287
+ A user-bound API key is a durable credential `dr` exchanges itself for a
288
+ short-lived JWT at `POST /jwt/api/apikey/token/`. There is no refresh token
289
+ and nothing to rotate on the client: the key is the only thing stored, and it
290
+ is exchanged again whenever the JWT is past half its lifetime.
291
+
292
+ Today a key comes from an operator calling the platform's internal
293
+ provisioning endpoint; there is no self-service `dr` command yet.
294
+
295
+ Save it once per person per machine:
296
+
297
+ ```
298
+ dr login --api-key --server US # hidden prompt on a terminal
299
+ printf '%s\n' "$DR_API_KEY" | dr login --api-key -s us # piped
300
+ ```
301
+
302
+ Piped, `-s` is required unless a credential file already names the server: the
303
+ interactive picker reads stdin too, and would swallow the key.
304
+
305
+ Piping keeps the key out of argv and shell history, but not out of a trace: don't
306
+ run the piped form with shell tracing (`set -x`), CI step-debug, or PowerShell
307
+ script-block logging enabled — any of those persists the key verbatim in a log.
308
+
309
+ The key is never taken as an argument (it would land in shell history and
310
+ `ps`). `dr` exchanges it once, reads your email from the returned JWT, and
311
+ stores the key in the keychain, or in the credential file when one is in use.
312
+ `dr logout` removes it. `dr login-as` cannot run from an API-key identity: the
313
+ server refuses to mint further credentials from an exchanged token.
314
+
315
+ For CI, write the credential file by hand instead of running any command:
316
+
317
+ ```json
318
+ { "apiKey": "<key>", "server": "US", "email": "svc@datarails.com" }
319
+ ```
320
+
321
+ `chmod 600` it right after writing, and keep it that way: the mode stays yours.
322
+ `dr` writes its own replacement at 0600 only when it saves a freshly exchanged
323
+ JWT — which is deferred for as long as an `access` token you provisioned
324
+ alongside the key is still fresh, and never happens at all if the key is bad,
325
+ revoked, or API keys are off for the org. A file left at 0644 can stay 0644
326
+ indefinitely. Keep it out of the repo checkout and out of any directory a
327
+ build-artifact or log-collection step will pick up.
328
+
329
+ Optionally add `"apiKeyExpiresAt": "<ISO date>"` — `YYYY-MM-DD`, or a full
330
+ timestamp carrying an explicit offset. It is read strictly, in the same form the
331
+ credential-file section above describes: an offset-less timestamp is rejected,
332
+ and a rejected value is never read. When the date is within 14 days, `dr` prints
333
+ a one-line
334
+ warning on stderr, once per identity: from every command that authenticates,
335
+ and from `whoami`, which is where an operator goes to inspect a credential.
336
+ `login`, `logout` and `login-as` do not show it. Keys expire and cannot be
337
+ extended, so rotation is create-new then revoke-old.
338
+
339
+ If the server answers 401, `dr` says the key **may** be revoked or expired,
340
+ API keys may not be enabled for your organization, or the server may be
341
+ temporarily unable to tell. It cannot distinguish these, and it never deletes
342
+ the stored key on failure. If you suspect a key has leaked, ask an admin to
343
+ revoke it and issue a new one — `dr` cannot do either itself.
344
+
345
+ ## Claude Code skills and plugins
346
+
347
+ Every skill lives under `plugins/<plugin>/skills/<name>/` and reaches people two ways:
348
+
349
+ - **Claude Code plugins** — the `dr-internal-plugins` marketplace lists three plugins
350
+ built from this repo at the `release` branch: `dr-cli` (install, doctor, call, the
351
+ update hook, the `dr-cli` command skill, `dr-switch-user`), `dr-cli-integrations`,
352
+ and `dr-cli-pat`. Install with
353
+ `/plugin install dr-cli@dr-internal-plugins`; a satellite pulls `dr-cli` in. Every
354
+ plugin version equals the CLI version, and the `dr-cli` hook installs that CLI.
355
+ - **The npm package** — `npm install -g @datarails-ai/dr-cli` embeds the same skills and
356
+ its postinstall writes them to `~/.claude/skills/<name>/` (plus a Gemini mirror when
357
+ `~/.gemini/` exists). On a machine where Claude Code has the `dr-cli` plugin, the
358
+ Claude-side write is skipped, and the copies an earlier install wrote for skills an
359
+ installed dr-cli plugin serves are removed (manifest-listed files only);
360
+ `dr skill install ~ --force` writes them anyway.
361
+
362
+ The three wrapper skills (`install`, `doctor`, `call`) carry a `.plugin-only` marker and
363
+ never embed into the binary.
364
+
365
+ Writing a new skill or a new plugin: [CONTRIBUTING.md](CONTRIBUTING.md).
366
+
367
+ ```
368
+ dr skill list # every embedded skill
369
+ dr skill install ~ # user-space install (plugin-managed machine: skipped, and the copies the plugins serve are removed)
370
+ dr skill print <name> # print an embedded skill's SKILL.md
371
+ ```
372
+
373
+ ## Command surface
374
+
375
+ | Area | Commands |
376
+ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
377
+ | Auth | `login`, `login-as <email>`, `logout`, `whoami` |
378
+ | Templates (tables) | `templates list/get/create/sample/distinct`, `templates aggregate` |
379
+ | Data models | `data-models list/get` |
380
+ | Datamappers | `datamappers list/get/create/update/delete` |
381
+ | Dashboards | `dashboards list/get/create/delete/prepare`, `dashboards set-date-picker <id> --mode <preset> \| --year YYYY \| --quarter YYYY-Qn \| --month YYYY-MM \| --day YYYY-MM-DD` |
382
+ | Functions | `functions list/get/create/delete` |
383
+ | Dynamic ranges | `dynamic-ranges list/get/data/create/delete` |
384
+ | Lookups (LUTs) | `lut list/create/update/delete/reorder/unmapped` (alias `lookups`) |
385
+ | Documents | `documents support-api set <id> --on/--off`, `documents calculate <id> --year/--quarter/--month/--day/--week ... [--backward] [--no-roll]` |
386
+ | Semantic tables | `semantic-tables list/get` |
387
+ | Storyboards | `storyboards list/get/slides/prepare/delete` |
388
+ | ExTables | `extables list/get/clone/delete` |
389
+ | Reports (Financials) | `reports list/get/clone/delete` |
390
+ | Data flow | `dataflow get` |
391
+ | Rescan / scan | `scans status <table_id>`, `rescan table` (refuses `UsageStats`) |
392
+ | Metrics / FX | `metrics …`, `fx …` |
393
+ | Features | `features …` |
394
+ | Users (IT) | `users list/get/me/can-login-as/set-email/bulk-set-email/bulk-create [--no-register-identity]/register-identity` |
395
+ | Organizations (IT) | `organizations list [--mini]`, `organizations get` (alias `orgs`) |
396
+ | Support Manager | `support-manager list [--search/--ordering/--page/--classification]` (alias `sm`) |
397
+ | Devices (IT) | `devices list/get/logs/received-logs/commands/download-logs/request-logs/command-delete` |
398
+ | Fileboxes | `filebox list/get/versions/sheets/preview/tags/create/upload/upload-version/update/archive/set-version-cycle` (alias `fileboxes`; `archive` is the rename of the former `delete` — server-side it's a soft-delete; `set-version-cycle` tags a version in a forecast/budget filebox with its cycle date + scenario, and `upload-version --date --scenario` does it at upload time) |
399
+ | Integrations | `integrations list/get/types/status/query-status/errors/sync/test-connection/add/update/delete/share` (alias `integration`) |
400
+ | Integrations (IT) | `integrations it list/get/add/update/delete/resync` (file-sync surface — OneDrive/SharePoint/Egnyte) |
401
+ | Widgets | `widgets list/get/data/create/update/delete` + flag-driven `widgets chart/kpi/pie/table/text` (alias `widget`; `data` applies the dashboard's saved filter bar + date picker by default so results match the product — `--no-dashboard-context` computes from the widget's own settings only) |
402
+ | Forecast | `forecast cycles list/get/documents/lock/unlock/set-default/roll-forward`, `forecast scenarios list/get/versions/create/delete/lock/unlock/set-default/clone/clone-from-budget/bulk-edit` |
403
+ | Collection processes | `collection-processes list/get/create/set-tag-config/add-fileboxes/add-folders/calculate` (alias `collection-process`; CPs are `COLLECTION` buckets containing N child fileboxes; `set-tag-config` updates the CP root document only — see `--help` for the per-child fan-out recipe; no `delete` verb) |
404
+ | Spend Control | `spend-control billing-frequencies list`, `spend-control contracts extract-from-files <files...>`, `spend-control contracts bulk-create --from <json>`, `spend-control vendors list/app-store/with-contracts/duplicates/categories`, `spend-control vendors owners <vendor_id>`, `spend-control vendors create <name>`, `spend-control vendors set-visibility <ids...> --visible <bool>`, `spend-control alerts list`, `spend-control alerts settings` |
405
+ | Workflows v2 | `workflow list/get/create/update/activate/deactivate/archive/restore/delete/clone`, `workflow process list/get/delete`, `workflow step get/create/update/delete/reorder`, `workflow task list/get/add-file/add-checklist/update/update-one/delete/notify/approve/reject/sign-off`, `workflow member list/check/add/update/remove/grant-bulk`, `workflow group list/add/update/remove/grant-bulk` (non-MEC; tenant must have `enable_workflow_v2`) |
406
+ | MEC Workflows v2 | `mec workflow list/get/create/update/activate/deactivate/archive/restore/delete/clone`, `mec workflow process list/get/delete`, `mec workflow step get/create/update/delete/reorder`, `mec workflow task list/get/add-file/add-checklist/update/update-one/delete/notify/approve/reject/sign-off` (file + checklist + `reconciliation_tags`), `mec workflow member list/check/add/update/remove/grant-bulk`, `mec workflow group list/add/update/remove/grant-bulk`, `mec workflow periods`, `mec workflow accounts tasks --date-tag <ts> --account-name <s> --account-group <s>`, `mec workflow accounts task-statuses --date-tag <ts>` (tenant must have `month_end_close`; type `"Month End Close"` enforced) |
407
+ | Desk | `desk teams list`, `desk support-email set <team> <email>`, `desk smtp set/rotate/get/test <team>`, `desk sendgrid register <domain>/status/verify`, `desk ticket create <team> --title --requester-email [--attach <path> ...] [--no-thread]` (`<team>` is an id, support email, or team name. `ticket create` **emails the requester**, so it is confirm-gated and keeps the word gate on prod — see `docs/how-to-create-a-desk-ticket.md`) |
408
+ | Connectors | `connectors publish <dataqueryFile>`, `connectors query repoint <queryId>`, `connectors binding get <fileboxUuid>` (drives the connectors control-plane over VPN) |
409
+ | Skill | `skill` — prints embedded `SKILL.md` |
410
+
411
+ ### Connectors: TLS over VPN
412
+
413
+ `dr connectors …` reaches the connectors control-plane
414
+ (`<env>-connectors-control-plane.datarails.io`) over the corporate VPN. Its
415
+ origin cert is a valid, publicly-trusted Let's Encrypt cert — but if your VPN
416
+ performs TLS inspection (e.g. Cloudflare WARP / Zero-Trust), it re-signs the
417
+ connection with a gateway CA that Node doesn't trust, and requests fail with
418
+ `self signed certificate in chain` / `unable to verify the first certificate`.
419
+
420
+ Fix it while keeping verification **on** — trust the VPN gateway root CA via
421
+ `NODE_EXTRA_CA_CERTS` (the CLI also appends these steps to the error inline):
422
+
423
+ ```powershell
424
+ # Windows — export the WARP/Gateway root CA from the trust store to a .pem, then:
425
+ setx NODE_EXTRA_CA_CERTS "C:\Users\<you>\gateway-ca.pem" # reopen the terminal
426
+ ```
427
+
428
+ ```bash
429
+ # macOS / Linux
430
+ export NODE_EXTRA_CA_CERTS=/path/to/gateway-ca.pem
431
+ ```
432
+
433
+ Do **not** use `NODE_TLS_REJECT_UNAUTHORIZED=0` — it disables verification
434
+ process-wide, including the monolith connection that carries your JWT and
435
+ refresh token.
436
+
437
+ ## Usage examples
438
+
439
+ Inspect a customer's environment:
440
+
441
+ ```
442
+ dr whoami --server US # who am I here?
443
+ dr templates list --server US # find the table
444
+ dr templates sample 3293 --rows 10 --server US # peek at data
445
+ dr dataflow get 3293 --server US # see where it flows
446
+ dr reports list --table 3293 --server US # which reports use it
447
+ ```
448
+
449
+ ## Development
450
+
451
+ ```
452
+ pnpm dev <args> # run src/cli.ts under tsx
453
+ pnpm typecheck # tsc --noEmit
454
+ pnpm lint
455
+ pnpm test # vitest
456
+ pnpm build # tsup → dist/cli.js; scripts/embed-skills.mjs → dist/skills/
457
+ ```
458
+
459
+ CI runs typecheck / lint / test / build on every push and PR
460
+ (`.github/workflows/ci.yml`). `node scripts/check-plugins.mjs` gates the
461
+ plugins tree (manifests, frontmatter, versions, the facts file, `CONTRIBUTING.md`);
462
+ `node --test plugins/dr-cli/hooks/tests/auto-update.test.js` runs the hook's
463
+ contract suite.
464
+
465
+ ### Adding a gated (destructive/sensitive) command
466
+
467
+ Sensitive writes route through `twoStepConfirm` (`src/commands/_confirm.ts`),
468
+ which requires a two-step confirm-token and — on production tenants — an
469
+ interactive challenge word for **Tier A** actions. Every gated action's
470
+ `(resource, verb)` must be classified in `src/commands/_prodGateTiers.ts`:
471
+
472
+ - **Tier A** — destructive / irreversible / org-wide → keeps the word gate on prod.
473
+ - **Tier B** — reversible / additive / low-blast → confirm-token only, no word.
474
+
475
+ Add the new `` `${resource}|${verb}` `` key to `PROD_GATE_TIER_A` or
476
+ `PROD_GATE_TIER_B`. If you skip it the test suite fails: `twoStepConfirm` throws
477
+ on an unclassified action under test, and a completeness test flags any new
478
+ gated resource or inline verb. (At runtime an unclassified action fail-closes
479
+ to Tier A, so prod is never left un-gated — the tests just force the decision
480
+ to be explicit.)
481
+
482
+ Note the flip side: **any** test that drives `twoStepConfirm` — even one
483
+ unrelated to the gate — throws under this strict classification if its
484
+ `(resource, verb)` is unclassified (`confirm action "…|…" has no explicit
485
+ production-gate tier`). Usually that means you genuinely added a gated action
486
+ and should classify its key. If instead you deliberately want an _unclassified_
487
+ action in a test (e.g. exercising the fail-closed path itself), disable the
488
+ check for that test with `_setStrictClassification(false)` from
489
+ `_confirm.ts` and restore it (`_setStrictClassification(undefined)`) afterward.
490
+
491
+ ## Troubleshooting
492
+
493
+ `dr-cli` is a thin command-line client over the **HTTP APIs of existing
494
+ Datarails services** — it holds almost no business logic of its own. Every
495
+ command maps to one or more endpoints on a service you can read the source of.
496
+ So when a command misbehaves, the bug is almost always on one of two sides:
497
+ how `dr-cli` builds the request, or how the service handles it. Both are open
498
+ to you.
499
+
500
+ The services `dr-cli` talks to:
501
+
502
+ | Service | What it serves | Repo |
503
+ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- |
504
+ | **Monolith** (the big one) | Most endpoints — tables, dashboards, functions, ranges, datamappers, LUTs, documents, storyboards, extables, reports, dataflow, scans, fileboxes, integrations, forecast, collection-processes, workflows/MEC, `cash`, users/orgs/support-manager/devices, features, `login`/`login-as`/`whoami` | [`dr-datarails-server`](https://github.com/Datarails/dr-datarails-server) |
505
+ | FinanceOS gateway | Paths under `/finance-os/api/…` — `templates sample`/`distinct`/`aggregate`, `semantic-tables`, `metrics`, `fx` | [`dr-finance-os`](https://github.com/Datarails/dr-finance-os) |
506
+ | Spend Control | `dr spend-control …` (`/api/spend-control/v1`) — a separate microservice reached through the monolith host | [`dr-spend-control`](https://github.com/Datarails/dr-spend-control) |
507
+ | Multi-Product | `dr multi-product …` (`/api/multi-product/v1`) — separate microservice, monolith host | [`dr-multi-product`](https://github.com/Datarails/dr-multi-product) |
508
+ | Desk | `dr desk …` (`/api/desk/v1/…`) — separate microservice, monolith host. One exception: `desk support-email set` writes to the MONOLITH's `/bo/teams/<id>/`, because that is where `support_email` lives | [`dr-desk-ai`](https://github.com/Datarails/dr-desk-ai) |
509
+ | Connectors control-plane | `dr connectors …` (reached over the corporate VPN) | [`dr-connectors-control-plane`](https://github.com/Datarails/dr-connectors-control-plane) |
510
+ | Onboarding-validation | `dr validate onboarding …` (its own host, `<env>-onboarding.datarails.com`) | [`dr-onboarding-validation-re`](https://github.com/Datarails/dr-onboarding-validation-re) |
511
+
512
+ > Not sure which service a command hits? Run it with `--json` and check the
513
+ > error, or read the header comment of the matching `src/api/<area>.ts` module
514
+ > — each one documents its endpoints. `dr kb …` is the exception: it talks to
515
+ > the Zendesk Help Center (plus an MCP server embedded in this repo), not a
516
+ > Datarails backend.
517
+
518
+ **Before you report a bug to the channel, do this first:**
519
+
520
+ 1. **Clone both repos** — this `dr-cli` repo _and_ the service repo the failing
521
+ command hits (from the table above; the monolith covers most cases).
522
+ 2. **Point Claude Code at both local checkouts** and ask it to investigate —
523
+ e.g. _"`dr functions create` returns a 400 on DEV; the CLI is in
524
+ `~/src/dr-cli`, the monolith is in `~/src/dr-datarails-server` — trace the
525
+ request from the CLI command through to the server view and find the
526
+ mismatch."_ With both sides on disk, Claude can follow the request end to
527
+ end: the flag parsing and payload shaping in `dr-cli`, and the serializer /
528
+ view / permission check on the service.
529
+ 3. **Ask it to implement or fix** the issue right there — in whichever repo the
530
+ root cause lives — rather than filing a report and waiting.
531
+
532
+ **We believe in contribution.** A reproduction with a root-cause pointer is
533
+ good; a PR is better. If you can fix it — in `dr-cli` or in the service — open
534
+ the PR (see [Contributing](#contributing) below) instead of only reporting it.
535
+ That's how the tool and the services it drives get better for the next person.
536
+
537
+ ## Contributing
538
+
539
+ Adding a skill or a plugin has its own checklist — see [CONTRIBUTING.md](CONTRIBUTING.md).
540
+ The loop below applies to every change.
541
+
542
+ The intended loop leans on Claude Code for review — a fresh session reviewing
543
+ a PR catches issues the authoring session is blind to, so the review and the
544
+ fix run in **separate sessions**.
545
+
546
+ 1. **Implement + open a PR.** Build the feature on a branch and open a PR
547
+ against `master`. Keep CI green (`typecheck` / `lint` / `test` / `build`).
548
+ 2. **Review in a clean session.** Clear the Claude session (or open a second
549
+ terminal) and ask for a code review of the PR — the clean context is what
550
+ makes the review useful. It routinely finds real issues.
551
+ 3. **Post the findings on the PR.** Read the review, then have Claude post the
552
+ issues as PR comments so there's a written record to work against.
553
+ 4. **Fix in the original session.** Back in the authoring terminal, clear the
554
+ session and tell Claude to fix the PR against those comments. Push the fixes.
555
+ 5. **Request review.** Assign a human reviewer (e.g. Aviad, Eyal).
556
+ 6. **Approve + merge.** After approval (or another round), merge to `master`.
557
+ Merging requires human approval and passing CI — never merge red.
558
+ 7. **Cut a release + update locally.** Run the release workflow (see
559
+ [Cutting a release](#cutting-a-release)) to bump the version, then update
560
+ your own machine by re-running the `npm install -g` line from
561
+ [Install](#install).
562
+
563
+ ## Cutting a release
564
+
565
+ The release workflow is [`.github/workflows/release.yml`](.github/workflows/release.yml).
566
+ It runs entirely from the GitHub Actions UI — nothing on your machine.
567
+
568
+ 1. Open https://github.com/Datarails/dr-cli/actions/workflows/release.yml
569
+ 2. Click **Run workflow**
570
+ 3. Pick a bump level (`patch`/`minor`/`major`) — or fill in `version` for
571
+ an explicit override (e.g. `1.0.0` for a milestone cut)
572
+ 4. Click green
573
+
574
+ The workflow then bumps `package.json`, commits + tags master, builds,
575
+ attaches `dr-v<TAG>.js` + `SKILL.md` + `README.md` to a new GitHub
576
+ Release with auto-generated changelog.
577
+
578
+ - Every `plugins/*/.claude-plugin/plugin.json` is stamped to the same version as `package.json`.
579
+ - Each plugin is tagged `<plugin>--v<version>`, alongside the CLI's own `v<version>` tag.
580
+ - As the last step, the `release` branch — the ref the `dr-internal-plugins` marketplace
581
+ tracks — is fast-forwarded to the new commit.
582
+
583
+ **App Space CLI bumps are automatic.** `@datarails-ai/dr-appspace-cli` is pinned to an exact
584
+ version. [`appspace-cli-bump.yml`](.github/workflows/appspace-cli-bump.yml) checks every 15
585
+ minutes for a newer one, pins it, runs the CI gates, and only when they pass pushes the pin to
586
+ master and starts this workflow with `patch`. A manual release works as before.
587
+
588
+ **JFrog publishing** is currently manual — after the release run lands,
589
+ build the npm tarball locally and upload it:
590
+
591
+ ```
592
+ pnpm install && pnpm build && npm pack
593
+ # upload dr-cli-<version>.tgz to JFrog
594
+ ```
595
+
596
+ Wiring `npm publish` into the release workflow is a follow-up — needs
597
+ a JFrog publish token as a repo secret (separate from the anonymous
598
+ read access end users use).
599
+
600
+ **Auth used by the release workflow:** the bump-and-push step uses
601
+ `secrets.BOT_PAT`, a fine-grained PAT scoped to this repo with
602
+ Contents: r/w. The PAT inherits the repo admin's branch-protection
603
+ bypass, so `git push origin master` from CI lands cleanly without
604
+ opening a PR.
605
+
606
+ ## License
607
+
608
+ Internal Datarails tool. Not for external distribution.