datagrok-tools 6.1.10 → 6.1.12

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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Datagrok-tools changelog
2
2
 
3
+ ## 6.1.11 (2026-04-27)
4
+
5
+ * `grok report read <path | instance number>` — normalize a report zip/json into one JSON object on stdout (envelope unwrap, `_meta.json` merge, optional `--extract-screenshot` / `--extract-d42` / `--extract-actions`)
6
+ * `grok report fetch` — single round-trip via the new `/reports/by-number/<number>/zip` endpoint, with fallback to the legacy search-then-download path on 404
7
+
3
8
  ## 6.1.10 (2026-04-17)
4
9
 
5
10
  * `grok s connections save` — create/update a connection from a JSON file (`--save-credentials` optional)
package/GROK_S.md ADDED
@@ -0,0 +1,361 @@
1
+ # Managing a Datagrok Server with `grok s`
2
+
3
+ The `grok server` command (alias `grok s`) talks to a running Datagrok instance over
4
+ its public REST API. It is the right tool for **any server-management task** that
5
+ does not require rendering the UI: creating users and groups, sharing entities,
6
+ running functions, browsing files, hitting raw API endpoints, or scripting bulk
7
+ imports.
8
+
9
+ Use this instead of UI automation (Playwright, Selenium) whenever the goal is data
10
+ management — it is faster, idempotent, scriptable, and does not depend on the
11
+ browser or a logged-in session.
12
+
13
+ ## When to use
14
+
15
+ | Task | Command |
16
+ |-------------------------------------------------------------|--------------------------------------------------------|
17
+ | Create / update a user | `grok s users save --json user.json` |
18
+ | Block / unblock a user | `grok s users block <login>` / `users unblock <login>` |
19
+ | Create / update a group | `grok s groups save --json group.json` |
20
+ | Add or remove users in a group | `grok s groups add-members <group> <user>...` |
21
+ | List members of a group | `grok s groups list-members <group>` |
22
+ | List the groups a user belongs to | `grok s groups list-memberships <user>` |
23
+ | Share a connection / query / script / project with a group | `grok s shares add <entity> <group> [--access View\|Edit]` |
24
+ | See who an entity is shared with | `grok s shares list <entity-id>` |
25
+ | Create / update a connection | `grok s connections save --json conn.json` |
26
+ | Test a connection | `grok s connections test <id-or-name>` |
27
+ | Run a registered function | `grok s functions run 'Pkg:fn(arg1,arg2)'` |
28
+ | List functions with rich filters | `grok s functions list --type script --language python --package Chem` |
29
+ | List / browse files in a file share | `grok s files list "System:AppData" -r` |
30
+ | Upload / download a table (CSV) | `grok s tables upload <name> file.csv` / `tables download <name> -O out.csv` |
31
+ | Check whether a package is deployed | `grok s packages list --filter "MyPlugin"` |
32
+ | Hit any undocumented endpoint | `grok s raw GET /api/users/current` |
33
+ | Check server + per-module health | `grok s healthcheck [--module <name>]` |
34
+ | Bulk operations in one round-trip | `grok s batch <entity> <verb> --json items.json` |
35
+
36
+ ## Configuration
37
+
38
+ Servers and credentials live in `~/.grok/config.yaml`:
39
+
40
+ ```yaml
41
+ default: local
42
+ servers:
43
+ local:
44
+ url: http://localhost:8888/api
45
+ key: admin
46
+ dev:
47
+ url: https://dev.datagrok.ai/api
48
+ key: <developer-key>
49
+ ```
50
+
51
+ - `grok config --server --alias <name> --server <url> --key <key>` writes a new entry.
52
+ - Add `--default` to make it the active server.
53
+ - Every `grok s ...` command accepts `--host <alias-or-url>` to override the default.
54
+
55
+ ## Entity operations
56
+
57
+ ### List / get / delete
58
+
59
+ ```bash
60
+ grok s users list # default: table, 50 rows
61
+ grok s users list --filter "login = 'admin'" # smart filter
62
+ grok s connections list --output json
63
+ grok s packages list --filter "name:MyPlugin" # table shows friendlyName
64
+ grok s groups get <id-or-name>
65
+ grok s connections delete <id>
66
+ ```
67
+
68
+ Options that work on every entity:
69
+
70
+ | Flag | Meaning |
71
+ |-----------------------|---------------------------------------------------|
72
+ | `--output table\|json\|csv\|quiet` | Output format; `quiet` prints ids only |
73
+ | `--filter "<expr>"` | Server-side smart filter |
74
+ | `--limit <n>` | Page size (default 50) |
75
+ | `--offset <n>` | Skip first `n` rows |
76
+ | `--host <alias\|url>` | Target a specific server from your config |
77
+
78
+ ### Save from JSON
79
+
80
+ `grok s users save` and `grok s groups save` accept the same JSON shape that the
81
+ REST API returns on `get`. The simplest valid bodies:
82
+
83
+ ```json
84
+ // user.json
85
+ { "#type": "User", "login": "alice.mendel", "firstName": "Alice", "lastName": "Mendel", "status": "active" }
86
+ ```
87
+
88
+ ```json
89
+ // group.json
90
+ { "#type": "UserGroup", "name": "Chemists", "friendlyName": "Chemists" }
91
+ ```
92
+
93
+ ```bash
94
+ grok s users save --json user.json
95
+ grok s groups save --json group.json --save-relations
96
+ ```
97
+
98
+ Include an `id` to update an existing entity; omit it to create. To introspect the
99
+ shape of an existing entity, use `grok s <entity> get <id-or-name> --output json`.
100
+
101
+ ### Connections
102
+
103
+ ```bash
104
+ grok s connections save --json conn.json --save-credentials
105
+ grok s connections test "MyUser:MyConnection" # by "author:name"
106
+ grok s connections test --json conn.json # test before saving
107
+ ```
108
+
109
+ See `public/packages/Chembl/connections/` for connection JSON examples.
110
+
111
+ ## Group membership
112
+
113
+ `grok s groups add-members` is idempotent — it resolves each member (by login, name,
114
+ or UUID), compares against the group's current children, and only writes when
115
+ something changes. Results are returned per member with statuses `added`, `updated`,
116
+ `noop`, `not-member`, or `error`.
117
+
118
+ ```bash
119
+ grok s groups add-members Chemists alice.mendel bob.curie
120
+ grok s groups add-members Chemists alice.mendel --admin # promote / add as admin
121
+ grok s groups add-members Admins analysts # nest a group inside a group
122
+ grok s groups add-members Chemists alice.mendel --user # force personal-group lookup
123
+ grok s groups remove-members Chemists alice.mendel
124
+ grok s groups list-members Chemists # all members
125
+ grok s groups list-members Chemists --admin # admin members only
126
+ grok s groups list-members Chemists --no-admin # non-admin members only
127
+ grok s groups list-memberships alice.mendel # groups a user belongs to
128
+ ```
129
+
130
+ When a name is ambiguous the command prints every matching group and exits non-zero —
131
+ pass a UUID to disambiguate, or add `--user` to restrict lookup to personal groups.
132
+
133
+ ## User administration
134
+
135
+ ```bash
136
+ grok s users block alice.mendel # accept login, UUID, or namespace:name
137
+ grok s users unblock alice.mendel
138
+ ```
139
+
140
+ Both commands resolve the argument to a full user record first, so the server receives
141
+ the entire object (matching the Python client's `grok.users.block(user)`). Blocking
142
+ flips `status` to `Blocked` and terminates active sessions; unblocking restores `Active`.
143
+
144
+ ## Sharing entities
145
+
146
+ ```bash
147
+ grok s shares add "MyUser:MyConnection" Chemists,Biologists --access Edit
148
+ grok s shares list <entity-uuid>
149
+ ```
150
+
151
+ The entity argument accepts either a UUID or an `"author:name"` pair. `--access`
152
+ defaults to `View`.
153
+
154
+ ## Running functions
155
+
156
+ ```bash
157
+ grok s functions run 'Chem:smilesToMw("CCO")' # positional args
158
+ grok s functions run 'Pkg:fn({smiles:"CCO", radius:2})' # named args
159
+ grok s functions run Pkg:fn --json params.json # big input from a file
160
+ ```
161
+
162
+ ## Listing functions with filters
163
+
164
+ `grok s functions list` composes a smart filter from flags so you don't have to hand-roll
165
+ `text=` expressions. All flags are optional and combine with `and`; `--filter` lets you
166
+ add any other smart-filter clause.
167
+
168
+ ```bash
169
+ grok s functions list --type script --language python # all python scripts
170
+ grok s functions list --type query --package Chembl # data-queries in one package
171
+ grok s functions list --package PowerPack --type package # package-bundled funcs only
172
+ grok s functions list --language r --limit 20 --output json
173
+ grok s functions list --type script --filter 'name contains "similarity"'
174
+ ```
175
+
176
+ Flag cheat sheet:
177
+
178
+ | Flag | Values | Notes |
179
+ |--------------|------------------------------------------------------------|-------------------------------------------------------------------|
180
+ | `--type` | `script`, `query` (alias for `data-query`), `function`, `package` | `function` matches both standalone and package-bundled funcs |
181
+ | `--language` | `python`, `r`, `julia`, `nodejs`, `octave`, `grok`, `javascript`, `pyodide` | Filters in the CLI (the server doesn't index Script.language); implies `--type script` when used alone |
182
+ | `--package` | package short name (e.g. `Chem`, `PowerPack`) | Maps to `package.shortName` in the smart filter |
183
+ | `--filter` | any smart-filter expression | Combined with the other flags via `and` |
184
+ | `--limit` / `--offset` | integers | Applied client-side — the public list endpoint returns the full match set |
185
+
186
+ ## Files
187
+
188
+ Remote paths are `<connector>/<file-path>`, where `<connector>` is the connection's
189
+ full name — including any namespace — e.g. `System:DemoFiles/smiles.csv`. The
190
+ separator between connector and path is the first `/`; the connector part may
191
+ contain colons (the namespace separator).
192
+
193
+ ```bash
194
+ grok s files list "System:AppData" -r # recursive
195
+ grok s files list "System:AppData/MyPlugin"
196
+ grok s files get "System:AppData/MyPlugin/config.json"
197
+ grok s files put ./smiles.csv "System:DemoFiles/smiles.csv" # upload local file
198
+ grok s files delete "System:AppData/MyPlugin/old.csv"
199
+ ```
200
+
201
+ `files put` streams the file as raw bytes (no base64), so it handles GB-scale uploads
202
+ without blowing up memory. Use `batch files.put` only when you want to bundle an
203
+ upload with other operations in a single round-trip (the batch path base64-encodes
204
+ the `source` before sending).
205
+
206
+ ## Tables
207
+
208
+ CSV-level table I/O — the shell counterpart to Python's `grok.tables.upload/download`.
209
+ Unlike `files put`, this registers a proper Datagrok table entity (returns `{ID, ...}`).
210
+
211
+ ```bash
212
+ grok s tables upload MyTable ./data.csv # CSV → table
213
+ grok s tables upload MyTable ./data.csv --output json # get ID and markup back
214
+ grok s tables download MyTable # CSV to stdout (pipe-friendly)
215
+ grok s tables download MyTable -O ./data.csv # CSV to a local file
216
+ grok s tables download <uuid> # UUID or namespace:name both work
217
+ ```
218
+
219
+ Upload streams raw bytes with `Content-Type: text/csv` so it handles large tables
220
+ without loading the whole file into a JSON envelope. `-O` / `--output-file` avoids
221
+ colliding with the format flag (`--output table|json|csv|quiet`), which still controls
222
+ how the upload result is printed.
223
+
224
+ ## Server health
225
+
226
+ ```bash
227
+ grok s healthcheck # full per-module health
228
+ grok s healthcheck --module scripting # filter to one module
229
+ grok s healthcheck --output json # machine-readable
230
+ ```
231
+
232
+ Hits `GET /public/v1/healthcheck`. Response:
233
+
234
+ ```json
235
+ {
236
+ "status": "ok",
237
+ "server": "https://public.datagrok.ai",
238
+ "version": "1.27",
239
+ "time": "2026-04-19T19:20:00.000Z",
240
+ "services": [
241
+ { "key": "scripting", "type": "Service", "name": "...", "status": "Running", "started": true, "enabled": true, "time": "..." }
242
+ ]
243
+ }
244
+ ```
245
+
246
+ `services` is the same payload as `/admin/health` (per-`GrokServiceInfo` records).
247
+ Requires a valid dev key (standard `grok s` auth). For an anonymous liveness probe —
248
+ load balancer, k8s readiness — hit `/admin/health` directly; it's on the server's
249
+ unauthenticated allowlist.
250
+
251
+ ## Raw API access
252
+
253
+ When no dedicated subcommand exists, fall through to `grok s raw`:
254
+
255
+ ```bash
256
+ grok s raw GET /api/users/current
257
+ grok s raw GET /api/packages/dev/MyPlugin
258
+ grok s raw POST /api/admin/reload-settings
259
+ ```
260
+
261
+ On **Windows Git Bash**, prefix raw paths with `MSYS_NO_PATHCONV=1` to stop the shell
262
+ from rewriting POSIX paths into Windows paths:
263
+
264
+ ```bash
265
+ MSYS_NO_PATHCONV=1 grok s raw GET /api/users/current
266
+ ```
267
+
268
+ ## Batch operations
269
+
270
+ Apply the same verb to many items in one round-trip:
271
+
272
+ ```bash
273
+ # Inline args
274
+ grok s batch files delete "System:AppData/old1.txt" "System:AppData/old2.txt"
275
+
276
+ # From a JSON array
277
+ grok s batch users save --json users.json # [{...user1}, {...user2}, ...]
278
+
279
+ # Full workflow manifest — mixed actions, optional transaction / stopOnError
280
+ grok s batch manifest.json
281
+ ```
282
+
283
+ Manifest shape:
284
+
285
+ ```json
286
+ {
287
+ "stopOnError": true,
288
+ "transaction": false,
289
+ "operations": [
290
+ { "id": "op1", "action": "users.save", "params": {"login": "alice", "firstName": "Alice"} },
291
+ { "id": "op2", "action": "groups.save", "params": {"name": "Chemists"} }
292
+ ]
293
+ }
294
+ ```
295
+
296
+ For `files.put`, add `"source": "<local-path>"` and the CLI base64-encodes the file
297
+ into `content` before sending.
298
+
299
+ ## Scripting pattern
300
+
301
+ `--output quiet` and `--output json` make `grok s` safe to compose with standard
302
+ shell tooling:
303
+
304
+ ```bash
305
+ # Pipe IDs
306
+ grok s users list --filter "status = 'active'" --output quiet \
307
+ | xargs -I{} grok s users get {}
308
+
309
+ # Filter with jq
310
+ grok s connections list --output json \
311
+ | jq '.[] | select(.dataSource=="Postgres") | .name'
312
+
313
+ # Generate JSON on the fly
314
+ for login in alice bob carol; do
315
+ printf '{"#type":"User","login":"%s","firstName":"%s","status":"active"}\n' \
316
+ "$login" "${login^}" > user.json
317
+ grok s users save --json user.json
318
+ done
319
+ ```
320
+
321
+ ## Worked example: seed users and populate groups
322
+
323
+ Create a group, bulk-create users, and drop each into the right group without touching
324
+ the UI:
325
+
326
+ ```bash
327
+ # 1. Create the group (idempotent if you include the existing id)
328
+ cat > /tmp/g.json <<'EOF'
329
+ { "#type": "UserGroup", "name": "Chemists", "friendlyName": "Chemists" }
330
+ EOF
331
+ grok s groups save --json /tmp/g.json
332
+
333
+ # 2. Create 8 users
334
+ for row in \
335
+ "alice.mendeleev:Alice:Mendeleev" \
336
+ "bob.curie:Bob:Curie" \
337
+ "carol.pauling:Carol:Pauling" ; do
338
+ IFS=: read -r login first last <<<"$row"
339
+ cat > /tmp/u.json <<EOF
340
+ { "#type": "User", "login": "$login", "firstName": "$first", "lastName": "$last", "status": "active" }
341
+ EOF
342
+ grok s users save --json /tmp/u.json --output quiet
343
+ done
344
+
345
+ # 3. Add them all to the group in one call (resolves logins to personal groups)
346
+ grok s groups add-members Chemists alice.mendeleev bob.curie carol.pauling --user
347
+
348
+ # 4. Verify
349
+ grok s groups list-members Chemists --no-admin
350
+ ```
351
+
352
+ Every subcommand above is idempotent — re-running the whole block is safe.
353
+
354
+ ## Implementation notes
355
+
356
+ - Source: `public/tools/bin/commands/server.ts`, `public/tools/bin/utils/node-dapi.ts`.
357
+ - The Node client talks directly to `/public/v1/` — no Dart interop, no browser, no
358
+ logged-in session required. Authentication uses the developer key from the config.
359
+ - If `grok s` is not working, start by running `grok s healthcheck` — it verifies the
360
+ URL, the key, and basic connectivity, and returns per-module status if the server is
361
+ reachable. Fall back to `grok s raw GET /api/users/current` to isolate auth issues.
@@ -46,7 +46,11 @@ function generateQueryWrappers() {
46
46
  const description = utils.getScriptDescription(q, utils.commentMap[utils.queryExtension]);
47
47
  const inputs = utils.getScriptInputs(q, utils.commentMap[utils.queryExtension]);
48
48
  const outputType = utils.getScriptOutputType(q, utils.commentMap[utils.queryExtension]);
49
- tb.replace('PARAMS_OBJECT', inputs).replace('TYPED_PARAMS', inputs).replace('FUNC_DESCRIPTION', description).replace('OUTPUT_TYPE', outputType === 'void' ? utils.dgToTsTypeMap['dataframe'] : outputType);
49
+ const resolvedOutputType = outputType === 'void' ? utils.dgToTsTypeMap['dataframe'] : outputType;
50
+ tb.replace('PARAMS_OBJECT', inputs).replace('TYPED_PARAMS', inputs).replace('FUNC_JSDOC', {
51
+ description,
52
+ inputs
53
+ }).replace('OUTPUT_TYPE', resolvedOutputType);
50
54
  wrappers.push(tb.build(1));
51
55
  }
52
56
  }
@@ -76,7 +80,10 @@ function generateScriptWrappers() {
76
80
  const tb = new utils.TemplateBuilder(utils.scriptWrapperTemplate).replace('FUNC_NAME', name).replace('FUNC_NAME_LOWERCASE', name).replace('PACKAGE_NAMESPACE', _package?.friendlyName ?? '');
77
81
  const inputs = utils.getScriptInputs(script);
78
82
  const outputType = utils.getScriptOutputType(script);
79
- tb.replace('PARAMS_OBJECT', inputs).replace('TYPED_PARAMS', inputs).replace('FUNC_DESCRIPTION', description).replace('OUTPUT_TYPE', outputType);
83
+ tb.replace('PARAMS_OBJECT', inputs).replace('TYPED_PARAMS', inputs).replace('FUNC_JSDOC', {
84
+ description,
85
+ inputs
86
+ }).replace('OUTPUT_TYPE', outputType);
80
87
  wrappers.push(tb.build(1));
81
88
  }
82
89
  }
@@ -100,7 +107,10 @@ function generateFunctionWrappers() {
100
107
  let outputType = '';
101
108
  outputType = annotationOutputDir ?? 'any';
102
109
  checkNameColision(name);
103
- const tb = new utils.TemplateBuilder(utils.scriptWrapperTemplate).replace('FUNC_NAME', name).replace('FUNC_NAME_LOWERCASE', name).replace('PACKAGE_NAMESPACE', _package?.friendlyName ?? '').replace('PARAMS_OBJECT', annotationInputs).replace('FUNC_DESCRIPTION', description).replace('TYPED_PARAMS', annotationInputs).replace('OUTPUT_TYPE', outputType);
110
+ const tb = new utils.TemplateBuilder(utils.scriptWrapperTemplate).replace('FUNC_NAME', name).replace('FUNC_NAME_LOWERCASE', name).replace('PACKAGE_NAMESPACE', _package?.friendlyName ?? '').replace('PARAMS_OBJECT', annotationInputs).replace('FUNC_JSDOC', {
111
+ description,
112
+ inputs: annotationInputs
113
+ }).replace('TYPED_PARAMS', annotationInputs).replace('OUTPUT_TYPE', outputType);
104
114
  wrappers.push(tb.build(1));
105
115
  }
106
116
  }
@@ -344,18 +344,28 @@ Examples:
344
344
  grok run https://my.datagrok.ai/api --key abc123
345
345
  `;
346
346
  const HELP_REPORT = `
347
- Usage: grok report <subcommand> <instance> <id>
347
+ Usage: grok report <subcommand> [args]
348
348
 
349
349
  Manage Datagrok user error reports
350
350
 
351
351
  Subcommands:
352
352
  fetch Download a report zip from a managed instance
353
+ read Normalize a report (zip or json) into one JSON object on stdout
353
354
  resolve Mark a report as resolved
354
355
  ticket Create a JIRA ticket for a report via the Datlas API
355
356
 
357
+ Read flags:
358
+ --extract-screenshot <path> Write the screenshot binary to <path>
359
+ --extract-d42 <dir> Unpack .d42 sidecar tables into <dir>
360
+ --extract-actions Write a sibling <stem>_actions.json
361
+
356
362
  Examples:
357
- grok report fetch dev 1528 Download report #1528 from the 'dev' instance
358
- grok report resolve dev 1528 Resolve report #1528 on the 'dev' instance
363
+ grok report fetch dev 1528 Download report #1528 from the 'dev' instance
364
+ grok report read /tmp/report.zip Print normalized JSON for a local zip
365
+ grok report read /tmp/report.json Print normalized JSON for a raw report.json
366
+ grok report read dev 1528 Fetch + normalize report #1528 from 'dev'
367
+ grok report read /tmp/report.zip --extract-screenshot ./shot.png
368
+ grok report resolve dev 1528 Resolve report #1528 on the 'dev' instance
359
369
  grok report ticket dev <report-uuid> Create a JIRA ticket for a report
360
370
 
361
371
  The instance name must match a server alias in ~/.grok/config.yaml.
@@ -367,7 +377,7 @@ Usage: grok server <entity> <verb> [args] [options]
367
377
  Manage a Datagrok server from the command line.
368
378
 
369
379
  Entities:
370
- users, groups, functions, connections, queries, scripts, packages, reports, files
380
+ users, groups, functions, connections, queries, scripts, packages, reports, files, tables
371
381
 
372
382
  Verbs:
373
383
  list List entities
@@ -376,17 +386,23 @@ Verbs:
376
386
 
377
387
  Special commands:
378
388
  grok s functions run <Name:func(args)> Call a function
389
+ grok s functions list [--type <t>] [--language <l>] [--package <p>] [--filter <expr>]
390
+ Type: script|query|function|package; language applies to scripts
379
391
  grok s files list [path] [-r] List files (recursive with -r)
380
392
  grok s shares add <entity> <group>[,<group>...] [--access View|Edit]
381
393
  Share an entity with groups
382
394
  grok s shares list <entity-id> List who an entity (UUID) is shared with
383
395
  grok s users save --json user.json Create or update a user from JSON
396
+ grok s users block <id-or-login> Block a user from the platform
397
+ grok s users unblock <id-or-login> Unblock a previously blocked user
384
398
  grok s groups save --json group.json [--save-relations]
385
399
  Create or update a group from JSON
386
400
  grok s connections save --json conn.json [--save-credentials]
387
401
  Create or update a connection from JSON
388
402
  grok s connections test <id-or-name> Test connectivity of an existing connection
389
403
  grok s connections test --json conn.json Test connectivity of a connection defined in JSON
404
+ grok s tables upload <name> <file.csv> Upload a CSV as a Datagrok table
405
+ grok s tables download <name-or-id> [-O <file>] Download a table as CSV (stdout by default)
390
406
  grok s raw <METHOD> <path> Hit any API endpoint
391
407
  grok s describe <entity-type> Show entity JSON schema
392
408