datagrok-tools 6.1.9 → 6.1.11
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 +15 -0
- package/CLAUDE.md +68 -0
- package/GROK_S.md +361 -0
- package/bin/__tests__/build.test.js +116 -0
- package/bin/__tests__/build.test.ts +101 -0
- package/bin/__tests__/node-dapi.connections.test.js +120 -0
- package/bin/__tests__/node-dapi.connections.test.ts +84 -0
- package/bin/__tests__/node-dapi.groups.test.js +467 -0
- package/bin/__tests__/node-dapi.groups.test.ts +298 -0
- package/bin/__tests__/node-dapi.integration.test.js +406 -0
- package/bin/__tests__/node-dapi.integration.test.ts +447 -0
- package/bin/__tests__/node-dapi.shares.test.js +107 -0
- package/bin/__tests__/node-dapi.shares.test.ts +70 -0
- package/bin/__tests__/node-dapi.users.test.js +86 -0
- package/bin/__tests__/node-dapi.users.test.ts +58 -0
- package/bin/__tests__/server-output.test.js +171 -0
- package/bin/__tests__/server-output.test.ts +133 -0
- package/bin/__tests__/server.test.js +277 -0
- package/bin/__tests__/server.test.ts +197 -0
- package/bin/commands/api.js +13 -3
- package/bin/commands/build.js +1 -1
- package/bin/commands/create.js +8 -5
- package/bin/commands/help.js +80 -4
- package/bin/commands/report.js +231 -36
- package/bin/commands/server.js +670 -0
- package/bin/grok.js +3 -1
- package/bin/utils/node-dapi.js +582 -0
- package/bin/utils/server-client.js +15 -0
- package/bin/utils/server-output.js +127 -0
- package/bin/utils/utils.js +35 -5
- package/package-template/package.json +1 -1
- package/package.json +10 -3
- package/vitest.config.ts +25 -0
package/bin/commands/help.js
CHANGED
|
@@ -13,7 +13,7 @@ Commands:
|
|
|
13
13
|
add Add an object template
|
|
14
14
|
api Create wrapper functions
|
|
15
15
|
build Build a package or multiple packages
|
|
16
|
-
check Check package content (function signatures,
|
|
16
|
+
check Check package content (function signatures, etgc.)
|
|
17
17
|
claude Launch Claude Code in a Datagrok dev container
|
|
18
18
|
config Create and manage config files
|
|
19
19
|
create Create a package
|
|
@@ -26,6 +26,7 @@ Commands:
|
|
|
26
26
|
test Run package tests
|
|
27
27
|
testall Run packages tests
|
|
28
28
|
migrate Migrate legacy tags to meta.role
|
|
29
|
+
server (s) Manage a Datagrok server (list/get/delete entities, run functions)
|
|
29
30
|
|
|
30
31
|
To get help on a particular command, use:
|
|
31
32
|
grok <command> --help
|
|
@@ -343,22 +344,95 @@ Examples:
|
|
|
343
344
|
grok run https://my.datagrok.ai/api --key abc123
|
|
344
345
|
`;
|
|
345
346
|
const HELP_REPORT = `
|
|
346
|
-
Usage: grok report <subcommand>
|
|
347
|
+
Usage: grok report <subcommand> [args]
|
|
347
348
|
|
|
348
349
|
Manage Datagrok user error reports
|
|
349
350
|
|
|
350
351
|
Subcommands:
|
|
351
352
|
fetch Download a report zip from a managed instance
|
|
353
|
+
read Normalize a report (zip or json) into one JSON object on stdout
|
|
352
354
|
resolve Mark a report as resolved
|
|
353
355
|
ticket Create a JIRA ticket for a report via the Datlas API
|
|
354
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
|
+
|
|
355
362
|
Examples:
|
|
356
|
-
grok report fetch dev 1528
|
|
357
|
-
grok report
|
|
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
|
|
358
369
|
grok report ticket dev <report-uuid> Create a JIRA ticket for a report
|
|
359
370
|
|
|
360
371
|
The instance name must match a server alias in ~/.grok/config.yaml.
|
|
361
372
|
`;
|
|
373
|
+
const HELP_SERVER = `
|
|
374
|
+
Usage: grok server <entity> <verb> [args] [options]
|
|
375
|
+
grok s <entity> <verb> [args] [options]
|
|
376
|
+
|
|
377
|
+
Manage a Datagrok server from the command line.
|
|
378
|
+
|
|
379
|
+
Entities:
|
|
380
|
+
users, groups, functions, connections, queries, scripts, packages, reports, files, tables
|
|
381
|
+
|
|
382
|
+
Verbs:
|
|
383
|
+
list List entities
|
|
384
|
+
get Get a single entity by ID or name
|
|
385
|
+
delete Delete an entity by ID
|
|
386
|
+
|
|
387
|
+
Special commands:
|
|
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
|
|
391
|
+
grok s files list [path] [-r] List files (recursive with -r)
|
|
392
|
+
grok s shares add <entity> <group>[,<group>...] [--access View|Edit]
|
|
393
|
+
Share an entity with groups
|
|
394
|
+
grok s shares list <entity-id> List who an entity (UUID) is shared with
|
|
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
|
|
398
|
+
grok s groups save --json group.json [--save-relations]
|
|
399
|
+
Create or update a group from JSON
|
|
400
|
+
grok s connections save --json conn.json [--save-credentials]
|
|
401
|
+
Create or update a connection from JSON
|
|
402
|
+
grok s connections test <id-or-name> Test connectivity of an existing connection
|
|
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)
|
|
406
|
+
grok s raw <METHOD> <path> Hit any API endpoint
|
|
407
|
+
grok s describe <entity-type> Show entity JSON schema
|
|
408
|
+
|
|
409
|
+
Options:
|
|
410
|
+
--host <alias|url> Server alias from config or full URL
|
|
411
|
+
--output <format> Output format: table (default), json, csv, quiet
|
|
412
|
+
--filter <text> Smart filter expression
|
|
413
|
+
--limit <n> Page size (default: 50)
|
|
414
|
+
--offset <n> Start offset (default: 0)
|
|
415
|
+
-r, --recursive Recursive (for files list)
|
|
416
|
+
--json <file> Read function parameters from JSON file
|
|
417
|
+
|
|
418
|
+
Examples:
|
|
419
|
+
grok s users list
|
|
420
|
+
grok s connections list --filter "PostgreSQL" --output json
|
|
421
|
+
grok s connections get <id>
|
|
422
|
+
grok s connections delete <id>
|
|
423
|
+
grok s connections save --json conn.json --save-credentials
|
|
424
|
+
grok s connections test "JohnDoe:MyConnection"
|
|
425
|
+
grok s connections test --json conn.json
|
|
426
|
+
grok s users save --json user.json
|
|
427
|
+
grok s groups save --json group.json --save-relations
|
|
428
|
+
grok s shares add "JohnDoe:MyConnection" Chemists,Admins --access Edit
|
|
429
|
+
grok s shares list <entity-uuid>
|
|
430
|
+
grok s functions run 'Chem:smilesToMw("ccc")'
|
|
431
|
+
grok s files list "System:AppData" -r
|
|
432
|
+
grok s raw GET /api/users/current
|
|
433
|
+
grok s describe connections
|
|
434
|
+
grok s users list --host dev
|
|
435
|
+
`;
|
|
362
436
|
const help = exports.help = {
|
|
363
437
|
add: HELP_ADD,
|
|
364
438
|
api: HELP_API,
|
|
@@ -376,5 +450,7 @@ const help = exports.help = {
|
|
|
376
450
|
test: HELP_TEST,
|
|
377
451
|
testall: HELP_TESTALL,
|
|
378
452
|
migrate: HELP_MIGRATE,
|
|
453
|
+
server: HELP_SERVER,
|
|
454
|
+
s: HELP_SERVER,
|
|
379
455
|
help: HELP
|
|
380
456
|
};
|
package/bin/commands/report.js
CHANGED
|
@@ -12,11 +12,14 @@ var color = _interopRequireWildcard(require("../utils/color-utils"));
|
|
|
12
12
|
var _testUtils = require("../utils/test-utils");
|
|
13
13
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
14
14
|
const fetch = require('node-fetch');
|
|
15
|
+
const AdmZip = require('adm-zip');
|
|
15
16
|
async function report(args) {
|
|
16
17
|
const subcommand = args._[1];
|
|
17
18
|
switch (subcommand) {
|
|
18
19
|
case 'fetch':
|
|
19
20
|
return await handleFetch(args);
|
|
21
|
+
case 'read':
|
|
22
|
+
return await handleRead(args);
|
|
20
23
|
case 'resolve':
|
|
21
24
|
return await handleResolve(args);
|
|
22
25
|
case 'ticket':
|
|
@@ -25,6 +28,53 @@ async function report(args) {
|
|
|
25
28
|
return false;
|
|
26
29
|
}
|
|
27
30
|
}
|
|
31
|
+
async function fetchReportMeta(url, token, number) {
|
|
32
|
+
const resp = await fetch(`${url}/reports?text=number%3D${encodeURIComponent(number)}`, {
|
|
33
|
+
headers: {
|
|
34
|
+
Authorization: token
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
if (!resp.ok) return null;
|
|
38
|
+
const arr = await resp.json();
|
|
39
|
+
if (!Array.isArray(arr) || arr.length === 0) return null;
|
|
40
|
+
return arr[0];
|
|
41
|
+
}
|
|
42
|
+
async function fetchReportBundle(instance, number) {
|
|
43
|
+
const {
|
|
44
|
+
url,
|
|
45
|
+
key
|
|
46
|
+
} = (0, _testUtils.getDevKey)(instance);
|
|
47
|
+
const token = await (0, _testUtils.getToken)(url, key);
|
|
48
|
+
const byNumberResp = await fetch(`${url}/reports/by-number/${encodeURIComponent(number)}/zip`, {
|
|
49
|
+
headers: {
|
|
50
|
+
Authorization: token
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
if (byNumberResp.ok) {
|
|
54
|
+
const zipBuffer = await byNumberResp.buffer();
|
|
55
|
+
const metaJson = await fetchReportMeta(url, token, number);
|
|
56
|
+
return {
|
|
57
|
+
zipBuffer,
|
|
58
|
+
metaJson
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (byNumberResp.status !== 404 && byNumberResp.status !== 405) throw new Error(`HTTP ${byNumberResp.status} from /reports/by-number/${number}/zip`);
|
|
62
|
+
const metaJson = await fetchReportMeta(url, token, number);
|
|
63
|
+
if (metaJson == null) throw new Error(`Report #${number} not found`);
|
|
64
|
+
const reportId = metaJson.id || metaJson.Id;
|
|
65
|
+
if (!reportId) throw new Error('Report found but has no id field');
|
|
66
|
+
const downloadResp = await fetch(`${url}/reports/${reportId}/zip`, {
|
|
67
|
+
headers: {
|
|
68
|
+
Authorization: token
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
if (!downloadResp.ok) throw new Error(`Report download failed (HTTP ${downloadResp.status})`);
|
|
72
|
+
const zipBuffer = await downloadResp.buffer();
|
|
73
|
+
return {
|
|
74
|
+
zipBuffer,
|
|
75
|
+
metaJson
|
|
76
|
+
};
|
|
77
|
+
}
|
|
28
78
|
async function handleFetch(args) {
|
|
29
79
|
const instance = args._[2];
|
|
30
80
|
const number = args._[3];
|
|
@@ -33,49 +83,194 @@ async function handleFetch(args) {
|
|
|
33
83
|
return false;
|
|
34
84
|
}
|
|
35
85
|
try {
|
|
86
|
+
console.log(`Fetching report #${number}...`);
|
|
36
87
|
const {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
} = (
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
if (!searchResp.ok) {
|
|
48
|
-
color.error(`Report search failed (HTTP ${searchResp.status})`);
|
|
49
|
-
return false;
|
|
88
|
+
zipBuffer,
|
|
89
|
+
metaJson
|
|
90
|
+
} = await fetchReportBundle(instance, number);
|
|
91
|
+
const outputPath = _path.default.join(_os.default.tmpdir(), `report_${instance}_${number}.zip`);
|
|
92
|
+
_fs.default.writeFileSync(outputPath, zipBuffer);
|
|
93
|
+
if (metaJson != null) {
|
|
94
|
+
const metaPath = outputPath.replace('.zip', '_meta.json');
|
|
95
|
+
_fs.default.writeFileSync(metaPath, JSON.stringify(metaJson, null, 2));
|
|
50
96
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
97
|
+
color.success(`Report saved to: ${outputPath}`);
|
|
98
|
+
console.log(outputPath);
|
|
99
|
+
return true;
|
|
100
|
+
} catch (err) {
|
|
101
|
+
color.error(`Error: ${err.message}`);
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function looksLikePath(s) {
|
|
106
|
+
return s.includes('/') || s.includes('\\') || /\.(zip|json)$/i.test(s) || _fs.default.existsSync(s);
|
|
107
|
+
}
|
|
108
|
+
function loadFromZip(z) {
|
|
109
|
+
const entries = z.getEntries();
|
|
110
|
+
const reportEntry = entries.find(e => e.entryName.toLowerCase().endsWith('report.json'));
|
|
111
|
+
if (reportEntry == null) {
|
|
112
|
+
const names = entries.map(e => e.entryName).join(', ');
|
|
113
|
+
throw new Error(`report.json not found in zip. Contents: ${names}`);
|
|
114
|
+
}
|
|
115
|
+
const data = JSON.parse(reportEntry.getData().toString('utf-8'));
|
|
116
|
+
const d42Names = entries.map(e => e.entryName).filter(n => n.toLowerCase().endsWith('.d42'));
|
|
117
|
+
return {
|
|
118
|
+
data,
|
|
119
|
+
zip: z,
|
|
120
|
+
d42Names
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function loadFromBuffer(buf) {
|
|
124
|
+
try {
|
|
125
|
+
const z = new AdmZip(buf);
|
|
126
|
+
return loadFromZip(z);
|
|
127
|
+
} catch {
|
|
128
|
+
const text = buf.toString('utf-8');
|
|
129
|
+
return {
|
|
130
|
+
data: JSON.parse(text),
|
|
131
|
+
zip: null,
|
|
132
|
+
d42Names: []
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function loadFromPath(p) {
|
|
137
|
+
if (p.toLowerCase().endsWith('.json')) {
|
|
138
|
+
const text = _fs.default.readFileSync(p, 'utf-8');
|
|
139
|
+
return {
|
|
140
|
+
data: JSON.parse(text),
|
|
141
|
+
zip: null,
|
|
142
|
+
d42Names: []
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return loadFromBuffer(_fs.default.readFileSync(p));
|
|
146
|
+
}
|
|
147
|
+
function unwrapEnvelope(data) {
|
|
148
|
+
if (data && typeof data === 'object' && '#type' in data && 'data' in data && typeof data.data === 'object' && data.data != null) {
|
|
149
|
+
const meta = {};
|
|
150
|
+
if (data.id != null) meta.id = data.id;
|
|
151
|
+
if (data.createdOn != null) meta.createdOn = data.createdOn;
|
|
152
|
+
if (data['#type'] != null) meta['#type'] = data['#type'];
|
|
153
|
+
return {
|
|
154
|
+
meta,
|
|
155
|
+
body: data.data
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
meta: {},
|
|
160
|
+
body: data
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function loadSidecar(inputPath) {
|
|
164
|
+
const stem = inputPath.replace(/\.[^./\\]+$/, '');
|
|
165
|
+
const sidecar = `${stem}_meta.json`;
|
|
166
|
+
if (sidecar !== inputPath && _fs.default.existsSync(sidecar)) return JSON.parse(_fs.default.readFileSync(sidecar, 'utf-8'));
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
function ensureParentDir(p) {
|
|
170
|
+
const dir = _path.default.dirname(p);
|
|
171
|
+
if (dir && dir !== '.' && !_fs.default.existsSync(dir)) _fs.default.mkdirSync(dir, {
|
|
172
|
+
recursive: true
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function extractScreenshot(zip, body, outPath) {
|
|
176
|
+
if (zip != null) {
|
|
177
|
+
const entries = zip.getEntries();
|
|
178
|
+
const e = entries.find(x => x.entryName.toLowerCase().endsWith('screenshot.png'));
|
|
179
|
+
if (e != null) {
|
|
180
|
+
ensureParentDir(outPath);
|
|
181
|
+
_fs.default.writeFileSync(outPath, e.getData());
|
|
182
|
+
return true;
|
|
55
183
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
184
|
+
}
|
|
185
|
+
const b64 = body && body.screenshot;
|
|
186
|
+
if (typeof b64 === 'string' && b64.length > 0) {
|
|
187
|
+
const stripped = b64.includes(',') ? b64.slice(b64.indexOf(',') + 1) : b64;
|
|
188
|
+
try {
|
|
189
|
+
const buf = Buffer.from(stripped, 'base64');
|
|
190
|
+
ensureParentDir(outPath);
|
|
191
|
+
_fs.default.writeFileSync(outPath, buf);
|
|
192
|
+
return true;
|
|
193
|
+
} catch {
|
|
60
194
|
return false;
|
|
61
195
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
function extractD42(zip, names, outDir) {
|
|
200
|
+
if (zip == null || names.length === 0) return [];
|
|
201
|
+
_fs.default.mkdirSync(outDir, {
|
|
202
|
+
recursive: true
|
|
203
|
+
});
|
|
204
|
+
const written = [];
|
|
205
|
+
for (var name of names) {
|
|
206
|
+
const entry = zip.getEntry(name);
|
|
207
|
+
if (entry == null) continue;
|
|
208
|
+
const out = _path.default.join(outDir, _path.default.basename(name));
|
|
209
|
+
_fs.default.writeFileSync(out, entry.getData());
|
|
210
|
+
written.push(out);
|
|
211
|
+
}
|
|
212
|
+
return written;
|
|
213
|
+
}
|
|
214
|
+
async function handleRead(args) {
|
|
215
|
+
const positional = args._.slice(2);
|
|
216
|
+
if (positional.length === 0) {
|
|
217
|
+
color.error('Usage: grok report read <path> | <instance> <number>');
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
const screenshotOut = args['extract-screenshot'];
|
|
221
|
+
const d42Dir = args['extract-d42'];
|
|
222
|
+
const extractActions = args['extract-actions'] === true;
|
|
223
|
+
try {
|
|
224
|
+
let loaded;
|
|
225
|
+
let sidecarMeta = null;
|
|
226
|
+
let inputPath = null;
|
|
227
|
+
let networkBase = null;
|
|
228
|
+
if (positional.length >= 2 && !looksLikePath(positional[0])) {
|
|
229
|
+
const [instance, number] = positional;
|
|
230
|
+
const bundle = await fetchReportBundle(instance, number);
|
|
231
|
+
loaded = loadFromBuffer(bundle.zipBuffer);
|
|
232
|
+
sidecarMeta = bundle.metaJson;
|
|
233
|
+
networkBase = _path.default.join(_os.default.tmpdir(), `report_${instance}_${number}`);
|
|
234
|
+
} else {
|
|
235
|
+
inputPath = _path.default.resolve(positional[0]);
|
|
236
|
+
if (!_fs.default.existsSync(inputPath)) {
|
|
237
|
+
color.error(`File not found: ${inputPath}`);
|
|
238
|
+
return false;
|
|
66
239
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
color.error(`Report download failed (HTTP ${downloadResp.status})`);
|
|
70
|
-
return false;
|
|
240
|
+
loaded = loadFromPath(inputPath);
|
|
241
|
+
sidecarMeta = loadSidecar(inputPath);
|
|
71
242
|
}
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
243
|
+
const {
|
|
244
|
+
meta: envelopeMeta,
|
|
245
|
+
body
|
|
246
|
+
} = unwrapEnvelope(loaded.data);
|
|
247
|
+
const meta = Object.assign({}, envelopeMeta, sidecarMeta || {});
|
|
248
|
+
const output = {
|
|
249
|
+
meta,
|
|
250
|
+
...body
|
|
251
|
+
};
|
|
252
|
+
const files = {};
|
|
253
|
+
if (screenshotOut) {
|
|
254
|
+
if (extractScreenshot(loaded.zip, body, screenshotOut)) {
|
|
255
|
+
files.screenshot = screenshotOut;
|
|
256
|
+
output.screenshot = screenshotOut;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (d42Dir) {
|
|
260
|
+
const written = extractD42(loaded.zip, loaded.d42Names, d42Dir);
|
|
261
|
+
if (written.length > 0) files.d42 = written;
|
|
262
|
+
}
|
|
263
|
+
if (extractActions && Array.isArray(body && body.actions)) {
|
|
264
|
+
const stem = inputPath != null ? inputPath.replace(/\.[^./\\]+$/, '') : networkBase;
|
|
265
|
+
if (stem != null) {
|
|
266
|
+
const actionsPath = `${stem}_actions.json`;
|
|
267
|
+
_fs.default.writeFileSync(actionsPath, JSON.stringify(body.actions, null, 2));
|
|
268
|
+
files.actions = actionsPath;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (Object.keys(files).length > 0) output.files = files;
|
|
272
|
+
process.stdout.write(JSON.stringify(output));
|
|
273
|
+
process.stdout.write('\n');
|
|
79
274
|
return true;
|
|
80
275
|
} catch (err) {
|
|
81
276
|
color.error(`Error: ${err.message}`);
|