@datarails-ai/dr-appspace-cli 0.9.1
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 +346 -0
- package/dist/cli.js +29461 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# dr-appspace — the Datarails App Space CLI
|
|
2
|
+
|
|
3
|
+
One binary, one job: **App Space** — build, try, push and publish App Space
|
|
4
|
+
apps. Nothing else of the Datarails system is exposed here; for everything
|
|
5
|
+
else, use `dr` (dr-cli). (The read-only dr-cli command groups this package
|
|
6
|
+
used to carry were removed — combining CLI capabilities is handled
|
|
7
|
+
elsewhere now.)
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm install -g @datarails-ai/dr-appspace-cli --registry https://datarails.jfrog.io/artifactory/api/npm/dr-cli-virtual
|
|
11
|
+
dr-appspace login -s DEV
|
|
12
|
+
dr-appspace app-space whoami -s DEV
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## What's inside
|
|
16
|
+
|
|
17
|
+
- **Auth — identical to dr-cli** (the code is a port of dr-cli's auth stack):
|
|
18
|
+
OAuth device-flow `login`, sessions in the OS keychain (service
|
|
19
|
+
`dr-appspace-cli`, keyed by `(server, email)`), automatic JWT refresh with
|
|
20
|
+
the full 401 recovery ladder, and the **credential-file store** for
|
|
21
|
+
keychain-less hosts (CI, containers, managed agents): when
|
|
22
|
+
`~/.dr/session.json` (or `$DR_CREDENTIALS_FILE`) exists it wins over the
|
|
23
|
+
keychain. The file contract is byte-compatible with dr-cli's
|
|
24
|
+
(`{access, refresh, server, email}`), so one provisioning step signs in
|
|
25
|
+
both CLIs — the same `dr_cli_auth.json` mount ai-service already provides
|
|
26
|
+
to managed agents works here unchanged.
|
|
27
|
+
- **`app-space` commands** (ported from `dr-dev-cli app-space`, re-based onto
|
|
28
|
+
this auth): `whoami`, `list`, `get`, `logs`, `try`, `push`, `publish`,
|
|
29
|
+
`unpublish` — plus `scaffold`, `preview`, `test`, `contract`, `compile` and
|
|
30
|
+
`eject`.
|
|
31
|
+
|
|
32
|
+
### A handler that takes a file
|
|
33
|
+
|
|
34
|
+
`try` and `test` can put a file in front of a handler, the way the page does:
|
|
35
|
+
upload it through the app's upload route, then send the signed reference as
|
|
36
|
+
`{"<field>": {"upload": "…"}}`.
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
dr-appspace app-space try upload_invoice --file pdf # the built-in sample invoice PDF
|
|
40
|
+
dr-appspace app-space try upload_invoice --file pdf=./acme-0417.pdf # a file of yours
|
|
41
|
+
dr-appspace app-space try upload_invoice --file pdf --payload @./payload.json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`--file <field>[=<path>]` is repeatable; a field that is not a `file` column
|
|
45
|
+
of any table in `data_model.json` gets a warning, not a refusal. `--payload
|
|
46
|
+
@<path>` reads the whole payload from a JSON file. Paths are relative to the
|
|
47
|
+
working directory. The sample invoice (`INV-0417`, Acme Office Supplies Ltd,
|
|
48
|
+
subtotal 165.00, tax 33.00, total 198.00) is a one-page ASCII PDF generated in
|
|
49
|
+
`src/commands/appspace/_samplePdf.ts`.
|
|
50
|
+
|
|
51
|
+
`test` hands the same sample PDF to every form's file field, and a
|
|
52
|
+
`tests.json` case may upload files before its dry-run:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{"handlers": {"upload_invoice": [
|
|
56
|
+
{"name": "reads the sample invoice", "payload": {}, "files": {"pdf": "sample"},
|
|
57
|
+
"shape": {"row": {"id": "string", "status": "string"}}}
|
|
58
|
+
]}}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
A `files` value is `"sample"` (reserved: the built-in sample invoice) or a
|
|
62
|
+
path inside the app folder; a path whose real location is outside the
|
|
63
|
+
folder, through a symlink too, is refused. Every file is read before any is
|
|
64
|
+
uploaded. Each upload stores a real file, as a browser upload does, so a
|
|
65
|
+
handler that hands it to an agent runs that agent on every `test`.
|
|
66
|
+
|
|
67
|
+
`compile` knows a `chart` component: a root element that draws the rows one
|
|
68
|
+
`list`, `inbox` or `custom` handler answers (`source`), as a `line`, `bar` or
|
|
69
|
+
`area` (`kind`), with `x` on the category axis, the one numeric column `y`,
|
|
70
|
+
an optional `series` column (one line per distinct value) and an optional
|
|
71
|
+
`format` (`currency`, `number`, `percent`; absent, a decimal `y` reads as
|
|
72
|
+
currency). Over a `list` or an `inbox` its columns are checked against the
|
|
73
|
+
table; over a `custom` handler `--check` prints `Not checked: chart columns
|
|
74
|
+
of …`, and `app-space test` checks what the handler answers: a `chart-empty`
|
|
75
|
+
finding when the source answered no rows (ungated for a custom handler; over
|
|
76
|
+
a list or an inbox, only when `seed.json` says it should have rows), no
|
|
77
|
+
`rows` array, or rows that carry no `x`/`series` key or no number under `y` —
|
|
78
|
+
naming the keys it did return (names only; no value leaves the run). Like
|
|
79
|
+
every finding it fails the run only under `--strict`. A page holding a
|
|
80
|
+
chart loads `echarts@5.6.0` and `dr-fmt@1.0.0` from the shelf before
|
|
81
|
+
`dr-app-runtime@1.1.5`. An older CLI's `get` drops the element.
|
|
82
|
+
|
|
83
|
+
A dashboard can be **assembled** rather than typed. `compile --draft
|
|
84
|
+
<spec-draft.json>` builds `contract.json` from the dashboard's skeleton
|
|
85
|
+
(`--skeleton <file>`, or the draft's own `skeleton` key) and one SPEC draft
|
|
86
|
+
per page — each card's `props` and SPEC `row`, plus its `page`, `reads`,
|
|
87
|
+
`datasets` and `capabilities` — and then compiles it like any other. The
|
|
88
|
+
frame is the CLI's: the first page is `ui/index.html` at `/`, every other
|
|
89
|
+
`ui/<name>.html` at `/<name>.html`, the skeleton's sections and card order,
|
|
90
|
+
a page not built yet carries one card saying it comes next, and — only
|
|
91
|
+
when the draft states it as `page.window` — the window every page opens on
|
|
92
|
+
(`mount.defaults.filters.period`), which `preview` and the post-push load
|
|
93
|
+
apply too. A draft or contract where every card opts out of the period bar
|
|
94
|
+
is refused (`assemble-period-override` / `period-override`). With a `contract.json` already in the folder a draft replaces only its
|
|
95
|
+
own page — except that a draft carrying `page.window` also moves every
|
|
96
|
+
page's opening window, because one `widget.js` serves them all. `--check` assembles in memory and writes nothing; a draft it cannot
|
|
97
|
+
place is `assemble-*`, exit 1. `--app-id <id>` names the app a first pass is
|
|
98
|
+
for, and `--expect <card>=<figure>` holds a card to the figure the user
|
|
99
|
+
confirmed once the package runs (exit 6 when it does not).
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
app-space ship [dir] --draft spec-draft-cfo.json --app-id app-7k2m3f --expect revenue=870,000
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`ship` is one pass of a dashboard build as one command — `compile --push`
|
|
106
|
+
over the assembled contract, R5 over every card, the `--expect` check — and
|
|
107
|
+
after a run that passed it prints the `appspace_app_update` block a build
|
|
108
|
+
agent's reply ends with, from the revision the push printed (`compile
|
|
109
|
+
--update-block` prints it too). After any failure it prints none.
|
|
110
|
+
|
|
111
|
+
`compile` also knows the two ways a file becomes the author's rather than
|
|
112
|
+
the compiler's, besides a `custom` handler:
|
|
113
|
+
|
|
114
|
+
- a **`custom` element** — `{"type": "custom", "props": {"module":
|
|
115
|
+
"components/<name>.js", "source": "<handler>", "args": {…}}}` — drawn by
|
|
116
|
+
`ui/components/<name>.js`, which calls `drApp.define("components/<name>.js",
|
|
117
|
+
{render(el, data, ui) {…}})` (`dr-app-runtime@1.1.6`; `ui` carries `args`,
|
|
118
|
+
`fmt`, `call`, `refresh`, `chart` and `theme`). `args` must be an object;
|
|
119
|
+
`module` is required.
|
|
120
|
+
- an **owned page** — `pages[i].owned: true` — whose markup is the file at its
|
|
121
|
+
path, carrying `<meta name="dr-owned" content="true">`. An optional
|
|
122
|
+
`pages[i].role` gates its nav entry, as a compiled page's screens do.
|
|
123
|
+
|
|
124
|
+
Both are compiler INPUTS, like a custom handler: they must be in the folder,
|
|
125
|
+
`compile` never writes them (not even under `--force`), records them under
|
|
126
|
+
`inputs` in `.appspace/compile.json`, never removes one as orphaned, and
|
|
127
|
+
`--check` carries them into its scratch package. A `ui/components/*.js` no
|
|
128
|
+
custom element names is refused like a stray handler — a push would ship it.
|
|
129
|
+
`app-space test` reports `custom-undefined` when a module never called
|
|
130
|
+
`drApp.define` for itself, `custom-failed` (with the module and the error)
|
|
131
|
+
when its component threw while drawing and the runtime drew its
|
|
132
|
+
`<module> failed: <message>` panel, and reads no layout rule into an owned
|
|
133
|
+
page. A custom element whose `declares.shows` names a number nothing inside
|
|
134
|
+
it projects once it drew (no `[data-dr-field]` for it) is
|
|
135
|
+
`custom-shows-unprojected` — a counted finding, so `--strict` fails on it: a
|
|
136
|
+
number no grader can read is not a measured one.
|
|
137
|
+
|
|
138
|
+
A card the contract gives no `props.label` is `card-untitled`, one per
|
|
139
|
+
element: the runtime titles it anyway — after its field, its id, or its
|
|
140
|
+
module, y column, source, entity (a form reads "New") — so a builder who
|
|
141
|
+
never named it ships a card called "Whatif":
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
[card-untitled] ui/cockpit.html → ui/cockpit.html#10: custom "whatif" has no label, so its card is titled "Whatif" after its id. Fix: give it props.label (what a reader should call this card). (contract.md §3.4 Pages)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
It reads the contract alone, for what a page draws as its own — its roots
|
|
148
|
+
and what sits in a region or a tab panel: `custom`, `chart`, `widget`,
|
|
149
|
+
`stat`, `list`, `queue`, `form`, and the parts that draw their label
|
|
150
|
+
(`dialog`, `number_input`, `select`, `date_picker`; a typed
|
|
151
|
+
`props.props.label`, or a dialog's `props.props.title`, names them). An
|
|
152
|
+
element's `card.title` names any of them (runtime 1.4.0's CardFrame draws
|
|
153
|
+
it); a widget's `props.title` does not — the runtime never reads it. A
|
|
154
|
+
field, a nav, a region, a tab panel, `tabs`, `data_grid` and
|
|
155
|
+
`variance_table` draw no title of their own and are never reported. Apps
|
|
156
|
+
only; counted, so `--strict` fails on it.
|
|
157
|
+
|
|
158
|
+
A page may declare its own params (`pages[i].params`, the seven kinds
|
|
159
|
+
`dr-app-runtime@1.4.0`'s param store reads) and a source take them by name
|
|
160
|
+
in `honors`. Every source handler of such an app carries `PARAMS` — every
|
|
161
|
+
page's declarations — into `sources.serve(…, TRANSFORMS, PARAMS)`, so a
|
|
162
|
+
transform's `params` holds only values the page declared; an app no page of
|
|
163
|
+
which declares one compiles exactly as before. A `dimensions:<Name>` honour
|
|
164
|
+
fills the source's `filterable`. `compile` refuses, with a pointer:
|
|
165
|
+
`param_custom_multi` / `param_custom_period` (a custom name of kind `multi`
|
|
166
|
+
or `period` — only `scenario`, `dimensions:<Name>` and `period` take those),
|
|
167
|
+
`param_kind_value` (a default, `min`/`max`/`step`, `values` or
|
|
168
|
+
`max_selected` its kind cannot hold), `param_conflict` (one name, two rules)
|
|
169
|
+
and `param_unknown_honor` (a source honours a name no page declares).
|
|
170
|
+
|
|
171
|
+
The look layer (runtime 1.4.0) is optional and per key: a band may state a
|
|
172
|
+
`subtitle`, a `layout` (`stack | grid | kpi_strip | split | sidebar |
|
|
173
|
+
hero`), a `surface` (`none | tinted | inverse`) and a sidebar's `aside`
|
|
174
|
+
(`start | end`); an element a `card` (`{variant: plain | outlined | elevated
|
|
175
|
+
| accent, title, subtitle}`) and an `emphasis` (`low | normal | high`).
|
|
176
|
+
`app.theme` (`{file: "ui/theme.css", tokens, preset?, density?}`) compiles
|
|
177
|
+
to `ui/theme.css` on `body.dr-app-page` in the `--dr-app-theme: full` form —
|
|
178
|
+
the `--dr-*` tokens and the shadcn triples computed from them by the
|
|
179
|
+
runtime's own map (`contract/themeMap.gen.mjs`, generated from
|
|
180
|
+
`tokens.mjs`) — linked after every shelf stylesheet; `preset` and `density`
|
|
181
|
+
become each page's `data-dr-theme` / `data-dr-density` where the page states
|
|
182
|
+
none. A theme `compile` cannot write is `theme_shape`.
|
|
183
|
+
|
|
184
|
+
`app-space test` holds every declared param to two counted findings, read
|
|
185
|
+
statically from the embedded contract and the pushed modules
|
|
186
|
+
(`contract/levers.mjs`, the App kit's A3.17/A3.18): `control-unbound` — no
|
|
187
|
+
control on the page writes it (no part in param mode, no custom whose module
|
|
188
|
+
calls `ui.setParam("<name>", …)` or whose `declares.produces` names it), so the
|
|
189
|
+
page only ever shows its default — and `param-unread` — no source a component
|
|
190
|
+
on the page reads honours it, directly or through what it is composed of, and
|
|
191
|
+
no custom module reads it off `ui.params`: moving it changes nothing. A page
|
|
192
|
+
with a module the run could not read, or a `ui.setParam` whose name is not a
|
|
193
|
+
literal, is not reported on. The App kit reports A3.18 as a violation and
|
|
194
|
+
A3.17 as a warning until dr-components ships param-mode controls.
|
|
195
|
+
|
|
196
|
+
It then RUNS each lever. On every page that declares params, once the page
|
|
197
|
+
has loaded (and before any form is submitted), `app-space test` moves one
|
|
198
|
+
param at a time through the page's own URL — `#p=` +
|
|
199
|
+
`encodeURIComponent(JSON)` of that one param, then `hashchange`, which
|
|
200
|
+
runtime 1.4.0's param store reads — and compares what the page draws with
|
|
201
|
+
what it drew at the defaults, NUMBERS only: every `data-dr-value` a part or
|
|
202
|
+
custom projects that reads as a number (never a control's own value, nor a
|
|
203
|
+
value whose field is the moved param), the figures in a stat's text (only
|
|
204
|
+
when the stat projects no `data-dr-value`; its dates and periods — `2026-01`,
|
|
205
|
+
`01/2026`, `Aug 2026`, `Q1 2026` — dropped, and any figure equal to the value
|
|
206
|
+
the param was at or moved to dropped as an echo), and the numbers in each
|
|
207
|
+
chart's series `data` as handed to echarts, gaps dropped. Labels never count
|
|
208
|
+
— a chart's categories, its series names, a month or a name in a cell or a
|
|
209
|
+
stat — so a start month that only relabels the months, every value the same,
|
|
210
|
+
is static. A number moves to its `max` (else double its default,
|
|
211
|
+
else default + `step`), a month one month on, a toggle flips, a select takes
|
|
212
|
+
another value, a multi another subset. A param whose first value moved no
|
|
213
|
+
number is moved once more — the other end of its range (else default +
|
|
214
|
+
`step`), a month one month back, another select value or subset — since a
|
|
215
|
+
lever can be inert at one end. Only a param neither value moved is
|
|
216
|
+
`lever-static`, counted: `ui/index.html: moving param "hires" (5 → 20, and
|
|
217
|
+
5 → 0) changed no number on the page — its sources honour it, but nothing
|
|
218
|
+
drawn depends on it. Fix: Check the transform reads params.get("hires")
|
|
219
|
+
(transforms/plan.py) …`. `--json` carries `levers: [{page, param, from, to,
|
|
220
|
+
second?, moved: [uid…]}]` (plus `inconclusive` for a lever it could not
|
|
221
|
+
judge). The moved reads go to the pushed app's real handlers, so the
|
|
222
|
+
transforms run with the moved params; a lever this run could not judge —
|
|
223
|
+
`--skip-ui`, a page that did not load, a move no source that honours the
|
|
224
|
+
param re-read after (a missed `hashchange`, a debounce past the grace — a
|
|
225
|
+
refetch of some other source does not count), a read that failed or was
|
|
226
|
+
still running with the param moved, a page that drew no number at its
|
|
227
|
+
defaults, a budget that ran out before the second value — is a `Not checked` line naming the param, never a pass and never
|
|
228
|
+
`lever-static`. Cost: one settle per declared param,
|
|
229
|
+
a second for each that moved nothing at its first value, plus one to move
|
|
230
|
+
back, on the page already mounted (the first render of each page only).
|
|
231
|
+
|
|
232
|
+
Roles are declared least senior first and the app's creator holds the last
|
|
233
|
+
one. `compile` refuses a list that reads the other way (`role-order`: the
|
|
234
|
+
default not first, or a creator's role that cannot call a handler the default
|
|
235
|
+
can), and `app-space test` reports `role-sees-nothing` when the app gates
|
|
236
|
+
components to roles and none of them to the role you hold — the page you open
|
|
237
|
+
is then only the ungated rest, although every render as the other roles
|
|
238
|
+
passes.
|
|
239
|
+
|
|
240
|
+
Before its findings, `app-space test` says how much of the pushed app is the
|
|
241
|
+
registry's and how much the builder's own code:
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
parts: 5 registry, 0 custom UI (0 lines), 0 custom handlers (0 lines), 2 transforms (30 lines) — 0% custom
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Custom UI is each `custom` element (with its modules' lines), a custom handler
|
|
248
|
+
each `custom`-role handler (with its lines), and a transform each file under
|
|
249
|
+
`transforms/` that `data.sources` names (once, however many sources share it,
|
|
250
|
+
by its non-blank lines — read out of the generated source handlers, which carry
|
|
251
|
+
it). The percent is custom UI's share of the components and nothing else.
|
|
252
|
+
`--json` carries the same counts as `parts`, the transforms as
|
|
253
|
+
`parts.transforms: {count, lines}`. A dashboard's line names no transforms.
|
|
254
|
+
|
|
255
|
+
`app-space eject <target> [--dir <dir>]` writes the starting file:
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
dr-appspace app-space eject handler:list_my_invoices # handlers/<name>.py as compiled, role "custom"
|
|
259
|
+
dr-appspace app-space eject page:ui/queue.html # the compiled page, marked owned; its elements leave the contract
|
|
260
|
+
dr-appspace app-space eject element:ui/overview.html#13 # a chart or a stat becomes a custom element + its module
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
It compiles in memory, writes only the ejected file and `contract.json`,
|
|
264
|
+
refuses a file already at that path unless it holds exactly what the
|
|
265
|
+
compiler would write, and refuses an eject after which the folder would not
|
|
266
|
+
compile (a page holding the only form a `by` handler is called from, a
|
|
267
|
+
handler a picker needs as a list). `page:` keeps the page's nav gate as
|
|
268
|
+
`pages[i].role`; an ejected handler's docstring says the file is yours.
|
|
269
|
+
The next `compile` carries the file as yours, and the one after writes
|
|
270
|
+
nothing.
|
|
271
|
+
|
|
272
|
+
### Which kit made the app: `push --kit`
|
|
273
|
+
|
|
274
|
+
`push` can name the builder kit that made the package, so the platform's
|
|
275
|
+
contract record (`app_contract.kit_version`) says which kit built each app
|
|
276
|
+
rather than the extractor's stamp, `dashboard-kit/contract@1`, which every
|
|
277
|
+
kit shares:
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
dr-appspace app-space push --kit appspace-app-kit@12
|
|
281
|
+
DR_APPSPACE_KIT=appspace-app-kit@12 dr-appspace app-space push
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The value travels as the push body's optional `kit` field. `--kit` wins over
|
|
285
|
+
`DR_APPSPACE_KIT`; with neither set (or either left blank) the field is left
|
|
286
|
+
out, and the platform records the contract's `generated_by` as before.
|
|
287
|
+
`compile --push`, `ship` and `compile --check` build the same body, so they
|
|
288
|
+
honour the variable too; they have no `--kit` flag of their own. The platform
|
|
289
|
+
checks the shape: `<name>@<number>`, the name in `a-z`, `0-9` and `-`, 64
|
|
290
|
+
characters at most. Anything else is a 422 naming that shape. The field is a
|
|
291
|
+
column on the record and nothing else: the contract document, the compiled
|
|
292
|
+
pages and the dashboard are the same with or without it. The builder flows
|
|
293
|
+
set `DR_APPSPACE_KIT` in their sandboxes.
|
|
294
|
+
|
|
295
|
+
**Push before `get`**: `get` replaces `handlers/` and `ui/` with what the
|
|
296
|
+
platform holds, and refuses (unless `--force`) while the compile record lists
|
|
297
|
+
files as yours that the pushed package does not hold as written.
|
|
298
|
+
|
|
299
|
+
This package ships **no skill and no generator**. The guidance an agent reads
|
|
300
|
+
lives in dr-assets-service, attached to the agent by id — see the repo's
|
|
301
|
+
`CLAUDE.md`. Nothing here installs, prints or bundles a `SKILL.md`, and no
|
|
302
|
+
command calls a model: the builders write `contract.json` themselves and run
|
|
303
|
+
`app-space compile`.
|
|
304
|
+
|
|
305
|
+
## Development
|
|
306
|
+
|
|
307
|
+
This package lives inside the dr-app-space repo but is independent of the
|
|
308
|
+
Python service: its own `package.json`, pnpm lockfile, and CI
|
|
309
|
+
(`.github/workflows/cli-checks.yml`).
|
|
310
|
+
|
|
311
|
+
```
|
|
312
|
+
cd cli
|
|
313
|
+
pnpm install
|
|
314
|
+
pnpm dev -- --help # run from source
|
|
315
|
+
pnpm typecheck && pnpm test
|
|
316
|
+
pnpm build # dist/cli.js
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Releasing
|
|
320
|
+
|
|
321
|
+
`.github/workflows/cli-release.yml` releases automatically on every push to
|
|
322
|
+
main that touches `cli/` (patch bump); manual dispatch remains for
|
|
323
|
+
minor/major or an explicit version. Flow: tests → resolve version → build →
|
|
324
|
+
publish to the `dr-cli-public` JFrog repo with `--ignore-scripts` → push a
|
|
325
|
+
`cli-v<semver>` tag (best-effort, traceability only). Versioning is
|
|
326
|
+
stateless because main's ruleset forbids direct pushes: no bump commit is
|
|
327
|
+
ever made — the next version is derived from the latest one already on the
|
|
328
|
+
registry, and `package.json`'s version only seeds the very first publish.
|
|
329
|
+
Consumers
|
|
330
|
+
install through `dr-cli-virtual`, which fronts `dr-cli-public` + public npm
|
|
331
|
+
with anonymous read — the same registry managed agents already use for
|
|
332
|
+
dr-cli, so no new JFrog infrastructure is needed.
|
|
333
|
+
|
|
334
|
+
Squat protection: like dr-cli, the scoped name means federation with public
|
|
335
|
+
npm is safe; if an unscoped placeholder is ever wanted on npmjs, publish it
|
|
336
|
+
manually (`0.0.1-locked`), as was done for `@datarails-ai/dr-cli`.
|
|
337
|
+
|
|
338
|
+
## Managed agents (later)
|
|
339
|
+
|
|
340
|
+
To expose this CLI on a managed agent the ai-service side needs the same
|
|
341
|
+
wiring dr-cli got under DR-50929 (`cliAgentDefinition.ts` and friends):
|
|
342
|
+
an install preamble (`npm install -g @datarails-ai/dr-appspace-cli --registry
|
|
343
|
+
.../dr-cli-virtual`), the mounted credential file (this CLI reads the exact
|
|
344
|
+
same `~/.dr/session.json` contract), and a token-refresh custom tool. Nothing
|
|
345
|
+
in this package blocks that — the credential-file store is the piece that
|
|
346
|
+
makes it possible, and it ships here from day one.
|