datagrok-tools 6.2.4 → 6.2.6
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 +13 -0
- package/GROK_S.md +11 -6
- package/bin/commands/help.js +1 -1
- package/bin/commands/report.js +16 -5
- package/bin/commands/server.js +5 -3
- package/bin/utils/node-dapi.js +7 -3
- package/download-demo-datasets.R +67 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Datagrok-tools changelog
|
|
2
2
|
|
|
3
|
+
## 6.2.6 (2026-05-26)
|
|
4
|
+
|
|
5
|
+
* `grok s tables upload` — accepts `.d42` binary blobs in addition to `.csv`. Content-Type is auto-detected from the file extension (`application/octet-stream` for d42, `text/csv` otherwise); server content-negotiates and persists either form against the same `/public/v1/tables/{name}` endpoint.
|
|
6
|
+
|
|
7
|
+
## 6.2.5 (2026-05-21)
|
|
8
|
+
|
|
9
|
+
* `grok report read` — renamed `--extract-actions` to `--extract-client-log`; sidecar is now `<stem>_client_log.json`. The old flag is no longer accepted.
|
|
10
|
+
* `grok report read` — legacy `actions` field in pre-consolidation report zips is folded into `clientLog` at read time (stderr warning emitted), so downstream consumers see one canonical field. Companion to the platform-side merge that drops `reports_data.actions` in favor of `client_log`.
|
|
11
|
+
|
|
12
|
+
## 6.2.4 (2026-05-13)
|
|
13
|
+
|
|
14
|
+
* GROK-20097: package template — replaced deprecated `"moduleResolution": "node"` in `tsconfig.json` with `"bundler"`, suppressing the TypeScript warning for new packages.
|
|
15
|
+
|
|
3
16
|
## 6.2.3 (2026-05-06)
|
|
4
17
|
|
|
5
18
|
* `grok test` — added `--skip-puppeteer` flag (symmetric counterpart of `--skip-playwright`). Bypasses `loadPackages()` and the Puppeteer/`DG.Test` runner so Playwright-only test directories (e.g. `public/playwright-public`) can run end-to-end via `grok test --skip-puppeteer` without tripping the Dart/JS package loader.
|
package/GROK_S.md
CHANGED
|
@@ -27,7 +27,7 @@ browser or a logged-in session.
|
|
|
27
27
|
| Run a registered function | `grok s functions run 'Pkg:fn(arg1,arg2)'` |
|
|
28
28
|
| List functions with rich filters | `grok s functions list --type script --language python --package Chem` |
|
|
29
29
|
| List / browse files in a file share | `grok s files list "System:AppData" -r` |
|
|
30
|
-
| Upload / download a table (CSV)
|
|
30
|
+
| Upload / download a table (CSV or d42) | `grok s tables upload <name> file.csv\|file.d42` / `tables download <name> -O out.csv` |
|
|
31
31
|
| Check whether a package is deployed | `grok s packages list --filter "MyPlugin"` |
|
|
32
32
|
| Hit any undocumented endpoint | `grok s raw GET /api/users/current` |
|
|
33
33
|
| Check server + per-module health | `grok s healthcheck [--module <name>]` |
|
|
@@ -205,21 +205,26 @@ the `source` before sending).
|
|
|
205
205
|
|
|
206
206
|
## Tables
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
Table I/O — the shell counterpart to Python's `grok.tables.upload/download`.
|
|
209
209
|
Unlike `files put`, this registers a proper Datagrok table entity (returns `{ID, ...}`).
|
|
210
210
|
|
|
211
211
|
```bash
|
|
212
212
|
grok s tables upload MyTable ./data.csv # CSV → table
|
|
213
|
+
grok s tables upload MyTable ./data.d42 # d42 binary → table
|
|
213
214
|
grok s tables upload MyTable ./data.csv --output json # get ID and markup back
|
|
214
215
|
grok s tables download MyTable # CSV to stdout (pipe-friendly)
|
|
215
216
|
grok s tables download MyTable -O ./data.csv # CSV to a local file
|
|
216
217
|
grok s tables download <uuid> # UUID or namespace:name both work
|
|
217
218
|
```
|
|
218
219
|
|
|
219
|
-
Upload streams raw bytes
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
220
|
+
Upload streams raw bytes — `Content-Type: text/csv` for `.csv` and
|
|
221
|
+
`application/octet-stream` for `.d42` (auto-detected from the file extension). Both
|
|
222
|
+
formats handle large tables without loading the whole file into a JSON envelope.
|
|
223
|
+
`-O` / `--output-file` avoids colliding with the format flag (`--output
|
|
224
|
+
table|json|csv|quiet`), which still controls how the upload result is printed.
|
|
225
|
+
|
|
226
|
+
Download is CSV-only — the server reads the stored d42 blob and converts. If you
|
|
227
|
+
need the raw d42 bytes, hit `/tables/data/<id>` directly via `grok s raw`.
|
|
223
228
|
|
|
224
229
|
## Server health
|
|
225
230
|
|
package/bin/commands/help.js
CHANGED
|
@@ -359,7 +359,7 @@ Subcommands:
|
|
|
359
359
|
Read flags:
|
|
360
360
|
--extract-screenshot <path> Write the screenshot binary to <path>
|
|
361
361
|
--extract-d42 <dir> Unpack .d42 sidecar tables into <dir>
|
|
362
|
-
--extract-
|
|
362
|
+
--extract-client-log Write a sibling <stem>_client_log.json
|
|
363
363
|
|
|
364
364
|
Examples:
|
|
365
365
|
grok report fetch dev 1528 Download report #1528 from the 'dev' instance
|
package/bin/commands/report.js
CHANGED
|
@@ -239,7 +239,7 @@ async function handleRead(args) {
|
|
|
239
239
|
}
|
|
240
240
|
const screenshotOut = args['extract-screenshot'];
|
|
241
241
|
const d42Dir = args['extract-d42'];
|
|
242
|
-
const
|
|
242
|
+
const extractClientLog = args['extract-client-log'] === true;
|
|
243
243
|
try {
|
|
244
244
|
let loaded;
|
|
245
245
|
let sidecarMeta = null;
|
|
@@ -265,6 +265,17 @@ async function handleRead(args) {
|
|
|
265
265
|
body
|
|
266
266
|
} = unwrapEnvelope(loaded.data);
|
|
267
267
|
const meta = Object.assign({}, envelopeMeta, sidecarMeta || {});
|
|
268
|
+
|
|
269
|
+
// Legacy-archive fallback: pre-consolidation report zips carry `actions`
|
|
270
|
+
// but no `clientLog`. Fold here so downstream consumers see one canonical
|
|
271
|
+
// field.
|
|
272
|
+
const hasClientLog = Array.isArray(body && body.clientLog) && body.clientLog.length > 0;
|
|
273
|
+
const hasLegacyActions = Array.isArray(body && body.actions) && body.actions.length > 0;
|
|
274
|
+
if (!hasClientLog && hasLegacyActions) {
|
|
275
|
+
body.clientLog = body.actions;
|
|
276
|
+
process.stderr.write('warning: report uses legacy `actions` field; treating as client log.\n');
|
|
277
|
+
}
|
|
278
|
+
if (body && 'actions' in body) delete body.actions;
|
|
268
279
|
const output = {
|
|
269
280
|
meta,
|
|
270
281
|
...body
|
|
@@ -280,12 +291,12 @@ async function handleRead(args) {
|
|
|
280
291
|
const written = extractD42(loaded.zip, loaded.d42Names, d42Dir);
|
|
281
292
|
if (written.length > 0) files.d42 = written;
|
|
282
293
|
}
|
|
283
|
-
if (
|
|
294
|
+
if (extractClientLog && Array.isArray(body && body.clientLog)) {
|
|
284
295
|
const stem = inputPath != null ? inputPath.replace(/\.[^./\\]+$/, '') : networkBase;
|
|
285
296
|
if (stem != null) {
|
|
286
|
-
const
|
|
287
|
-
_fs.default.writeFileSync(
|
|
288
|
-
files.
|
|
297
|
+
const clientLogPath = `${stem}_client_log.json`;
|
|
298
|
+
_fs.default.writeFileSync(clientLogPath, JSON.stringify(body.clientLog, null, 2));
|
|
299
|
+
files.clientLog = clientLogPath;
|
|
289
300
|
}
|
|
290
301
|
}
|
|
291
302
|
if (Object.keys(files).length > 0) output.files = files;
|
package/bin/commands/server.js
CHANGED
|
@@ -8,6 +8,7 @@ exports.parseFuncCall = parseFuncCall;
|
|
|
8
8
|
exports.resolveManifestSources = resolveManifestSources;
|
|
9
9
|
exports.server = server;
|
|
10
10
|
var fs = _interopRequireWildcard(require("fs"));
|
|
11
|
+
var path = _interopRequireWildcard(require("path"));
|
|
11
12
|
var _nodeDapi = require("../utils/node-dapi");
|
|
12
13
|
var _serverClient = require("../utils/server-client");
|
|
13
14
|
var _serverOutput = require("../utils/server-output");
|
|
@@ -169,14 +170,15 @@ async function handleTablesDownload(dapi, rest, argv, output) {
|
|
|
169
170
|
async function handleTablesUpload(dapi, rest, output) {
|
|
170
171
|
const [name, localPath] = rest;
|
|
171
172
|
if (!name || !localPath) {
|
|
172
|
-
(0, _serverOutput.printError)(new Error('Usage: grok s tables upload <name> <file.csv>'));
|
|
173
|
+
(0, _serverOutput.printError)(new Error('Usage: grok s tables upload <name> <file.csv|file.d42>'));
|
|
173
174
|
return false;
|
|
174
175
|
}
|
|
175
176
|
if (!fs.existsSync(localPath)) {
|
|
176
177
|
(0, _serverOutput.printError)(new Error(`Local file not found: ${localPath}`));
|
|
177
178
|
return false;
|
|
178
179
|
}
|
|
179
|
-
const
|
|
180
|
+
const ct = path.extname(localPath).toLowerCase() === '.d42' ? 'application/octet-stream' : 'text/csv';
|
|
181
|
+
const result = await dapi.tables.upload(name, localPath, ct);
|
|
180
182
|
if (output === 'quiet') console.log(result?.ID ?? result?.id ?? '');else (0, _serverOutput.printOutput)(result, output);
|
|
181
183
|
return true;
|
|
182
184
|
}
|
|
@@ -611,7 +613,7 @@ Special commands:
|
|
|
611
613
|
grok s groups list-memberships <group> [--admin] List parent groups
|
|
612
614
|
grok s users block <id-or-login> Block a user from the platform
|
|
613
615
|
grok s users unblock <id-or-login> Unblock a previously blocked user
|
|
614
|
-
grok s tables upload <name> <file.csv>
|
|
616
|
+
grok s tables upload <name> <file.csv|file.d42> Upload a CSV or d42 binary as a Datagrok table
|
|
615
617
|
grok s tables download <name-or-id> [-O <file>] Download a table as CSV (stdout by default)
|
|
616
618
|
grok s batch <entity> <verb> arg1 [arg2 ...] Batch operation (one round-trip)
|
|
617
619
|
grok s batch <entity> <verb> --json params.json Batch from JSON array
|
package/bin/utils/node-dapi.js
CHANGED
|
@@ -502,12 +502,16 @@ class NodeTablesDataSource {
|
|
|
502
502
|
return result;
|
|
503
503
|
}
|
|
504
504
|
|
|
505
|
-
/**
|
|
506
|
-
|
|
505
|
+
/**
|
|
506
|
+
* POST /public/v1/tables/{name} with raw bytes. Returns `{ID, Grok name, Markup, URL}`.
|
|
507
|
+
* Defaults to `text/csv`; pass `application/octet-stream` to upload a `.d42`
|
|
508
|
+
* binary blob — the server content-negotiates on the header and persists either form.
|
|
509
|
+
*/
|
|
510
|
+
async upload(name, localPath, contentType = 'text/csv') {
|
|
507
511
|
const fs = require('fs');
|
|
508
512
|
const bytes = fs.readFileSync(localPath);
|
|
509
513
|
const seg = encodeURIComponent(name.replace(/:/g, '.'));
|
|
510
|
-
return this.client.putBytes(`/public/v1/tables/${seg}`, bytes,
|
|
514
|
+
return this.client.putBytes(`/public/v1/tables/${seg}`, bytes, contentType);
|
|
511
515
|
}
|
|
512
516
|
}
|
|
513
517
|
exports.NodeTablesDataSource = NodeTablesDataSource;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# tools/download-demo-datasets.R
|
|
2
|
+
#
|
|
3
|
+
# Выгружает три демо-датасета для ANOVA UI:
|
|
4
|
+
# - InsectSprays (R datasets package) → CSV
|
|
5
|
+
# - ToothGrowth (R datasets package) → CSV
|
|
6
|
+
# - SiRstv (NIST StRD) → CSV
|
|
7
|
+
#
|
|
8
|
+
# Запуск из корня репозитория:
|
|
9
|
+
# Rscript tools/download-demo-datasets.R
|
|
10
|
+
#
|
|
11
|
+
# Результат сохраняется в packages/EDA/demo-data/.
|
|
12
|
+
|
|
13
|
+
OUT_DIR <- "packages/EDA/demo-data"
|
|
14
|
+
dir.create(OUT_DIR, showWarnings = FALSE, recursive = TRUE)
|
|
15
|
+
|
|
16
|
+
# ─── 1. InsectSprays ──────────────────────────────────────────────────
|
|
17
|
+
data(InsectSprays)
|
|
18
|
+
write.csv(InsectSprays,
|
|
19
|
+
file.path(OUT_DIR, "insect-sprays.csv"),
|
|
20
|
+
row.names = FALSE,
|
|
21
|
+
fileEncoding = "UTF-8")
|
|
22
|
+
cat("Wrote insect-sprays.csv (", nrow(InsectSprays), "rows )\n")
|
|
23
|
+
|
|
24
|
+
# ─── 2. ToothGrowth ───────────────────────────────────────────────────
|
|
25
|
+
data(ToothGrowth)
|
|
26
|
+
write.csv(ToothGrowth,
|
|
27
|
+
file.path(OUT_DIR, "tooth-growth.csv"),
|
|
28
|
+
row.names = FALSE,
|
|
29
|
+
fileEncoding = "UTF-8")
|
|
30
|
+
cat("Wrote tooth-growth.csv (", nrow(ToothGrowth), "rows )\n")
|
|
31
|
+
|
|
32
|
+
# ─── 3. SiRstv (NIST StRD) ────────────────────────────────────────────
|
|
33
|
+
# NIST .dat файл имеет большой текстовый header; данные идут после
|
|
34
|
+
# строки "Data: Instrument Resistance" (заголовок встроен в маркер).
|
|
35
|
+
nist_url <- "https://www.itl.nist.gov/div898/strd/anova/SiRstv.dat"
|
|
36
|
+
# На Windows libcurl падает на NIST SSL (CRYPT_E_NO_REVOCATION_CHECK).
|
|
37
|
+
# wininet использует системный HTTP-стек и работает; на других ОС — стандартный путь.
|
|
38
|
+
read_nist <- function(u) {
|
|
39
|
+
if (.Platform$OS.type == "windows") {
|
|
40
|
+
tmp <- tempfile()
|
|
41
|
+
suppressWarnings(download.file(u, tmp, quiet = TRUE, method = "wininet"))
|
|
42
|
+
readLines(tmp)
|
|
43
|
+
} else {
|
|
44
|
+
readLines(url(u))
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
lines <- read_nist(nist_url)
|
|
48
|
+
data_start <- grep("^\\s*Data:\\s+Instrument", lines)
|
|
49
|
+
if (length(data_start) != 1)
|
|
50
|
+
stop("Cannot locate 'Data: Instrument' marker in SiRstv.dat")
|
|
51
|
+
|
|
52
|
+
# Пропустить только строку "Data:..." — следующая уже содержит данные.
|
|
53
|
+
data_lines <- lines[(data_start + 1):length(lines)]
|
|
54
|
+
data_lines <- data_lines[nzchar(trimws(data_lines))]
|
|
55
|
+
|
|
56
|
+
# В каждой строке два числа через whitespace: Instrument Resistance
|
|
57
|
+
sirstv <- read.table(text = data_lines,
|
|
58
|
+
col.names = c("Instrument", "Resistance"))
|
|
59
|
+
sirstv$Instrument <- as.integer(sirstv$Instrument)
|
|
60
|
+
|
|
61
|
+
write.csv(sirstv,
|
|
62
|
+
file.path(OUT_DIR, "silicon-resistivity.csv"),
|
|
63
|
+
row.names = FALSE,
|
|
64
|
+
fileEncoding = "UTF-8")
|
|
65
|
+
cat("Wrote silicon-resistivity.csv (", nrow(sirstv), "rows )\n")
|
|
66
|
+
|
|
67
|
+
cat("\nAll three demo datasets written to", OUT_DIR, "\n")
|
package/package.json
CHANGED