@datarails-ai/dr-cli 0.0.1-locked → 1.6.70
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.
- package/README.md +608 -0
- package/dist/chunk-DWMCBGQH.js +242 -0
- package/dist/cli.js +64468 -0
- package/dist/kb-6UWI7Q32.js +21 -0
- package/dist/scopes-map.json +4412 -0
- package/dist/skills/connector-sandbox/SKILL.md +95 -0
- package/dist/skills/datarails-connect-workato/SKILL.md +140 -0
- package/dist/skills/datarails-connectors-onboard-connector/SKILL.md +813 -0
- package/dist/skills/dr-cli/SKILL.md +659 -0
- package/dist/skills/dr-cli/reference/admin.md +796 -0
- package/dist/skills/dr-cli/reference/budget-builder.md +151 -0
- package/dist/skills/dr-cli/reference/calc-vals.md +73 -0
- package/dist/skills/dr-cli/reference/calcmodels.md +76 -0
- package/dist/skills/dr-cli/reference/cash.md +61 -0
- package/dist/skills/dr-cli/reference/dashboards-widgets.md +85 -0
- package/dist/skills/dr-cli/reference/datamappers.md +150 -0
- package/dist/skills/dr-cli/reference/desk.md +472 -0
- package/dist/skills/dr-cli/reference/filebox-documents.md +373 -0
- package/dist/skills/dr-cli/reference/forecast.md +51 -0
- package/dist/skills/dr-cli/reference/integrations.md +57 -0
- package/dist/skills/dr-cli/reference/lookups.md +168 -0
- package/dist/skills/dr-cli/reference/metrics.md +512 -0
- package/dist/skills/dr-cli/reference/multi-product.md +180 -0
- package/dist/skills/dr-cli/reference/reconciliation-mec.md +76 -0
- package/dist/skills/dr-cli/reference/rh-config.md +84 -0
- package/dist/skills/dr-cli/reference/safety.md +143 -0
- package/dist/skills/dr-cli/reference/spend-control.md +206 -0
- package/dist/skills/dr-cli/reference/templates.md +163 -0
- package/dist/skills/dr-cli/reference/workflows.md +137 -0
- package/dist/skills/dr-error-enrichment/SKILL.md +80 -0
- package/dist/skills/dr-error-enrichment/reference/core.md +257 -0
- package/dist/skills/dr-error-enrichment/reference/groundcover.md +84 -0
- package/dist/skills/dr-error-enrichment/reference/jira-bugs.md +219 -0
- package/dist/skills/dr-error-enrichment/reference/safety-backup.md +122 -0
- package/dist/skills/dr-error-enrichment-author/SKILL.md +219 -0
- package/dist/skills/dr-error-enrichment-improve/SKILL.md +218 -0
- package/dist/skills/dr-error-enrichment-maintain/SKILL.md +237 -0
- package/dist/skills/dr-error-enrichment-reconcile/SKILL.md +167 -0
- package/dist/skills/dr-filebox-api-setup/SKILL.md +170 -0
- package/dist/skills/dr-switch-user/SKILL.md +395 -0
- package/dist/skills/dr-validate/SKILL.md +392 -0
- package/dist/skills/dr-validate/report-template.html +950 -0
- package/dist/skills/fx-conversion-matrix/SKILL.md +763 -0
- package/dist/skills/fx-conversion-matrix/scripts/_args.mjs +52 -0
- package/dist/skills/fx-conversion-matrix/scripts/_xlsx.mjs +241 -0
- package/dist/skills/fx-conversion-matrix/scripts/build-matrix.mjs +366 -0
- package/dist/skills/fx-conversion-matrix/scripts/gen-mappers.mjs +171 -0
- package/dist/skills/fx-conversion-matrix/scripts/rates-to-xlsx.mjs +172 -0
- package/dist/skills/fx-onboard/SKILL.md +325 -0
- package/dist/skills/mec-table-validation/SKILL.md +383 -0
- package/dist/skills/plugins.json +24 -0
- package/dist/skills/validate-workato-migration/SKILL.md +167 -0
- package/dist/skills/validate-workato-migration/reference/gotchas.md +16 -0
- package/dist/skills/validate-workato-migration/reference/staging-orgs.md +26 -0
- package/dist/skills/validate-workato-migration/scripts/compare_files.py +255 -0
- package/dist/zendeskKb-JPSHVOPH.js +243 -0
- package/package.json +56 -9
- 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.
|