gulp-cli 1.1.4__tar.gz → 1.1.6__tar.gz
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.
- {gulp_cli-1.1.4/src/gulp_cli.egg-info → gulp_cli-1.1.6}/PKG-INFO +2 -1
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/README.md +1 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/docs/command-reference.md +40 -12
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/docs/examples.md +14 -4
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/_version.py +3 -3
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/cli.py +14 -16
- gulp_cli-1.1.6/src/gulp_cli/commands/enrich.py +187 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/ingest.py +696 -138
- gulp_cli-1.1.6/src/gulp_cli/commands/utility.py +21 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/config.py +4 -0
- gulp_cli-1.1.6/src/gulp_cli/version.py +20 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6/src/gulp_cli.egg-info}/PKG-INFO +2 -1
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli.egg-info/SOURCES.txt +9 -1
- gulp_cli-1.1.6/tests/test_enrich_live.py +302 -0
- gulp_cli-1.1.6/tests/test_ingest_paths_file.py +78 -0
- gulp_cli-1.1.6/tests/test_ingest_wait_log.py +258 -0
- gulp_cli-1.1.6/tests/test_version.py +48 -0
- gulp_cli-1.1.6/tests/test_zip_create.py +272 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/.github/workflows/portable-bundles.yml +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/.github/workflows/python-package.yml +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/.gitignore +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/GULP_DUAL_LICENSE.txt +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/LICENSE.AGPL-3.0.txt +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/LICENSE.md +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/docs/extensions.md +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/docs/getting-started.md +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/docs/portable.md +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/docs/resource-management.md +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/docs/troubleshooting-cli.md +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/gulp-cli.spec +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/portable/launch-linux.sh +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/portable/launch-macos.sh +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/portable/launch-windows.bat +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/pyproject.toml +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/setup.cfg +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/__init__.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/__main__.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/client.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/__init__.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/acl.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/auth.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/collab.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/context.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/db.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/enhance_map.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/glyph.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/mapping.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/operations.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/plugin.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/query.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/source.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/stats.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/storage.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/user_group.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/commands/users.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/extension/__init__.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/extension/query_sigma_zip.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/extension/story.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/extension_helpers.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/extensions.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/output.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli/utils.py +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli.egg-info/dependency_links.txt +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli.egg-info/entry_points.txt +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli.egg-info/requires.txt +0 -0
- {gulp_cli-1.1.4 → gulp_cli-1.1.6}/src/gulp_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gulp-cli
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.6
|
|
4
4
|
Summary: Command-line client for gULP
|
|
5
5
|
Author-email: Mentat <info@mentat.is>
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -57,6 +57,7 @@ cd gulp-cli && pip install -e .
|
|
|
57
57
|
|
|
58
58
|
# Verify installation
|
|
59
59
|
gulp-cli --help
|
|
60
|
+
gulp-version
|
|
60
61
|
```
|
|
61
62
|
|
|
62
63
|
### Portable Bundles
|
|
@@ -25,6 +25,18 @@ gulp-cli --verbose --as-user USER_NAME <group> <command> ...
|
|
|
25
25
|
|
|
26
26
|
## Core Commands
|
|
27
27
|
|
|
28
|
+
### `utility`
|
|
29
|
+
|
|
30
|
+
Utility commands for gULP server metadata and similar endpoints.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
gulp-cli utility gulp-version
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`utility gulp-version` prints only the gULP version and uses the saved CLI session, so run `gulp-cli auth login` first.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
28
40
|
### Authentication (`auth`)
|
|
29
41
|
|
|
30
42
|
#### `login`
|
|
@@ -437,20 +449,21 @@ gulp-cli operation revoke-user my_investigation alice
|
|
|
437
449
|
Ingest one or more files into an operation.
|
|
438
450
|
|
|
439
451
|
```bash
|
|
440
|
-
gulp-cli ingest file OPERATION_ID PLUGIN
|
|
452
|
+
gulp-cli ingest file OPERATION_ID PLUGIN [FILE...] [OPTIONS]
|
|
441
453
|
```
|
|
442
454
|
|
|
443
455
|
**Arguments:**
|
|
444
456
|
|
|
445
457
|
- `OPERATION_ID` — Target operation
|
|
446
458
|
- `PLUGIN` — Plugin name (e.g., `win_evtx`, `syslog`, `csv`)
|
|
447
|
-
- `FILE` — File path or glob pattern (multiple allowed)
|
|
459
|
+
- `FILE` — File path or glob pattern (multiple allowed; optional when `--paths-file` is set)
|
|
448
460
|
|
|
449
461
|
**Options:**
|
|
450
462
|
|
|
451
|
-
- `--context
|
|
463
|
+
- `--context TEXT` — Context for ingestion, specify a name to create a new context if it doesn't exist, or an existing `context_id` (default: `sdk_context`)
|
|
452
464
|
- `--plugin-params TEXT` — JSON string with plugin parameters
|
|
453
465
|
- `--flt TEXT` — JSON object for `GulpIngestionFilter`
|
|
466
|
+
- `--paths-file PATH` — Text file with one file or glob pattern per line. When set, positional `FILE` arguments are optional and ignored if also provided.
|
|
454
467
|
- `--reset-operation` — Delete and recreate the operation before ingest starts
|
|
455
468
|
- `--create-operation` — Create operation automatically when it does not exist
|
|
456
469
|
- `--preview` — Run preview-only ingestion (no persistence)
|
|
@@ -474,6 +487,9 @@ gulp-cli ingest file my_op win_evtx 'samples/win_evtx/*.evtx'
|
|
|
474
487
|
# Multiple patterns
|
|
475
488
|
gulp-cli ingest file my_op win_evtx '**/*.evtx' '/logs/*.json'
|
|
476
489
|
|
|
490
|
+
# Paths file
|
|
491
|
+
gulp-cli ingest file my_op win_evtx --paths-file /tmp/file_paths.txt
|
|
492
|
+
|
|
477
493
|
# With plugin parameters
|
|
478
494
|
gulp-cli ingest file my_op csv file.csv --plugin-params '{"delimiter":";","encoding":"utf-8"}'
|
|
479
495
|
|
|
@@ -494,18 +510,20 @@ gulp-cli ingest file my_op win_evtx 'samples/win_evtx/*.evtx' --wait
|
|
|
494
510
|
Add files to an existing source.
|
|
495
511
|
|
|
496
512
|
```bash
|
|
497
|
-
gulp-cli ingest file-to-source SOURCE_ID
|
|
513
|
+
gulp-cli ingest file-to-source SOURCE_ID [FILE...] [OPTIONS]
|
|
498
514
|
```
|
|
499
515
|
|
|
500
516
|
**Arguments:**
|
|
501
517
|
|
|
502
518
|
- `SOURCE_ID` — Source ID
|
|
503
|
-
- `FILE` — File path or glob pattern (multiple allowed)
|
|
519
|
+
- `FILE` — File path or glob pattern (multiple allowed; optional when `--paths-file` is set)
|
|
504
520
|
|
|
505
521
|
**Options:**
|
|
506
522
|
|
|
507
|
-
- `--plugin
|
|
523
|
+
- `--plugin TEXT` — Override the plugin associated with the source (requires `--plugin-params`)
|
|
524
|
+
- `--plugin-params TEXT` — JSON object for plugin_params (overrides source defaults; required when `--plugin` is set, `{}` is allowed)
|
|
508
525
|
- `--flt TEXT` — JSON object for GulpIngestionFilter
|
|
526
|
+
- `--paths-file PATH` — Text file with one file or glob pattern per line. When set, positional `FILE` arguments are optional and ignored if also provided.
|
|
509
527
|
- `--wait` — Wait for completion with progress
|
|
510
528
|
- `--show-per-file-progress / --no-show-per-file-progress` — Show one line per completed file request in `--wait` mode (default: on)
|
|
511
529
|
- `--timeout INTEGER` — Timeout in seconds for `--wait` mode
|
|
@@ -516,6 +534,8 @@ gulp-cli ingest file-to-source SOURCE_ID FILE [FILE...] [OPTIONS]
|
|
|
516
534
|
```bash
|
|
517
535
|
gulp-cli ingest file-to-source source123 /new/System.evtx --wait
|
|
518
536
|
gulp-cli ingest file-to-source source123 '/new/*.evtx'
|
|
537
|
+
gulp-cli ingest file-to-source source123 --paths-file /tmp/file_paths.txt
|
|
538
|
+
gulp-cli ingest file-to-source source123 /new/events.json --plugin json --plugin-params '{"mapping_parameters": {}}'
|
|
519
539
|
```
|
|
520
540
|
|
|
521
541
|
---
|
|
@@ -537,7 +557,7 @@ gulp-cli ingest zip OPERATION_ID ZIP_FILE [OPTIONS]
|
|
|
537
557
|
|
|
538
558
|
**Options:**
|
|
539
559
|
|
|
540
|
-
- `--context
|
|
560
|
+
- `--context TEXT` — Context for ingestion, specify a name to create a new context if it doesn't exist, or an existing `context_id` (default: `sdk_context`)
|
|
541
561
|
- `--flt TEXT` — JSON object for `GulpIngestionFilter`
|
|
542
562
|
- `--create-operation` — Create operation automatically when it does not exist
|
|
543
563
|
- `--wait` — Wait for completion
|
|
@@ -576,7 +596,12 @@ gulp-cli ingest zip-create OUTPUT_ZIP [PATH_PATTERN...] [OPTIONS]
|
|
|
576
596
|
**Options:**
|
|
577
597
|
|
|
578
598
|
- `--paths-file TEXT` — Text file with one source path expression per line (supports environment variables and `~` per line)
|
|
579
|
-
- `--overwrite` —
|
|
599
|
+
- `--no-overwrite` — Do not overwrite output ZIP if it already exists (default: overwrite enabled)
|
|
600
|
+
- `--no-preserve-path` — Do not preserve the source path hierarchy inside archive entries (default: preserve path enabled)
|
|
601
|
+
- `--split-size TEXT` — Create multipart ZIP volumes at this size (bytes, `kb`, `mb`, `gb`; default: disabled)
|
|
602
|
+
|
|
603
|
+
When `--split-size` is set, `zip-create` writes multipart ZIP volumes such as `evidence.zip.001`, `evidence.zip.002`, etc. Extract those volumes with `7z` or another tool that supports split ZIP files.
|
|
604
|
+
When `--paths-file` is set, positional `PATH_PATTERN` values are ignored. Without `--paths-file`, positional `PATH_PATTERN` values are used.
|
|
580
605
|
|
|
581
606
|
**Examples:**
|
|
582
607
|
|
|
@@ -585,10 +610,10 @@ gulp-cli ingest zip-create OUTPUT_ZIP [PATH_PATTERN...] [OPTIONS]
|
|
|
585
610
|
gulp-cli ingest zip-create /tmp/evidence.zip /data/host1/*.evtx /data/host2 /data/readme.txt
|
|
586
611
|
|
|
587
612
|
# Use environment variables and home shortcuts
|
|
588
|
-
gulp-cli ingest zip-create '$CASE_DIR/evidence.zip' '$CASE_DIR/raw/*.json' '~/pcaps/*.pcap'
|
|
613
|
+
gulp-cli ingest zip-create '$CASE_DIR/evidence.zip' '$CASE_DIR/raw/*.json' '~/pcaps/*.pcap'
|
|
589
614
|
|
|
590
615
|
# Read source path expressions from a file
|
|
591
|
-
gulp-cli ingest zip-create /tmp/evidence.zip --paths-file /tmp/evidence_paths.txt
|
|
616
|
+
gulp-cli ingest zip-create /tmp/evidence.zip --paths-file /tmp/evidence_paths.txt
|
|
592
617
|
```
|
|
593
618
|
|
|
594
619
|
---
|
|
@@ -1298,6 +1323,7 @@ gulp-cli enrich documents OPERATION_ID [OPTIONS]
|
|
|
1298
1323
|
- `--plugin-params TEXT` — Plugin parameters JSON
|
|
1299
1324
|
- `--flt TEXT` — Filter JSON
|
|
1300
1325
|
- `--wait` — Wait for completion
|
|
1326
|
+
- `--fields TEXT` — { "field": "value" } pairs to set on enriched documents (JSON string): if "value" is `null` the enrichment is made on field's value in the document. either, enriched "value" is used.
|
|
1301
1327
|
|
|
1302
1328
|
**Examples:**
|
|
1303
1329
|
|
|
@@ -1305,7 +1331,8 @@ gulp-cli enrich documents OPERATION_ID [OPTIONS]
|
|
|
1305
1331
|
gulp-cli enrich documents my_op \
|
|
1306
1332
|
--plugin my_enricher \
|
|
1307
1333
|
--plugin-params '{"api_key":"..."}' \
|
|
1308
|
-
--flt '{"
|
|
1334
|
+
--flt '{"operation_ids":["my_op"],"source_ids":["security"]}' \
|
|
1335
|
+
--fields '{"ip.destination": null}'
|
|
1309
1336
|
```
|
|
1310
1337
|
|
|
1311
1338
|
---
|
|
@@ -1327,12 +1354,13 @@ gulp-cli enrich single-id OPERATION_ID DOCUMENT_ID [OPTIONS]
|
|
|
1327
1354
|
|
|
1328
1355
|
- `--plugin TEXT` — Plugin name (required)
|
|
1329
1356
|
- `--plugin-params TEXT` — Plugin parameters JSON
|
|
1357
|
+
- `--fields TEXT` — { "field": "value" } pairs to set on enriched documents (JSON string): if "value" is `null` the enrichment is made on field's value in the document. either, enriched "value" is used.
|
|
1330
1358
|
- `--wait` — Wait for completion
|
|
1331
1359
|
|
|
1332
1360
|
**Examples:**
|
|
1333
1361
|
|
|
1334
1362
|
```bash
|
|
1335
|
-
gulp-cli enrich single-id my_op doc123 --plugin my_enricher
|
|
1363
|
+
gulp-cli enrich single-id my_op doc123 --plugin my_enricher --fields '{"host.os": null}' --wait
|
|
1336
1364
|
```
|
|
1337
1365
|
|
|
1338
1366
|
---
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
- [Run Rule on Specific Source](#run-rule-on-specific-source)
|
|
46
46
|
- [Multiple Sigma Rules in Batch](#multiple-sigma-rules-in-batch)
|
|
47
47
|
- [Enrichment \& Tagging Workflows](#enrichment--tagging-workflows)
|
|
48
|
+
- [use whois to enrich IP addresses with domain information](#use-whois-to-enrich-ip-addresses-with-domain-information)
|
|
48
49
|
- [Tag Suspicious Events](#tag-suspicious-events)
|
|
49
50
|
- [Apply Threat Level Classification](#apply-threat-level-classification)
|
|
50
51
|
- [Track Analysis Progress](#track-analysis-progress)
|
|
@@ -174,8 +175,7 @@ gulp-cli ingest file incident-001 csv /data/access_log.csv \
|
|
|
174
175
|
--plugin-params '{"delimiter":";","encoding":"iso-8859-1","has_header":true}'
|
|
175
176
|
|
|
176
177
|
# pass mapping directly without using a mapping file
|
|
177
|
-
gulp-cli ingest file test_operation csv ./samples/mftecmd/sample_record.csv --plugin-params '{ "mapping_parameters": { "mappings": { "
|
|
178
|
-
_mapping": { "fields": { "Created0x10": { "ecs": [ "@timestamp" ] } } } } } }' --reset-operation --wait
|
|
178
|
+
gulp-cli ingest file test_operation csv ./samples/mftecmd/sample_record.csv --plugin-params '{ "mapping_parameters": { "mappings": { "test_mapping": { "fields": { "Created0x10": { "ecs": ["@timestamp" ] } } } } } }' --reset-operation --wait
|
|
179
179
|
|
|
180
180
|
# pass mapping using a gulp mapping file with mapping_id to specify which mapping to use in the file
|
|
181
181
|
gulp-cli ingest file test_operation csv ./samples/mftecmd/sample_record.csv --plugin-params '{ "mapping_parameters"
|
|
@@ -219,11 +219,11 @@ gulp-cli ingest zip incident-001 /evidence/evidence.zip --create-operation
|
|
|
219
219
|
|
|
220
220
|
```bash
|
|
221
221
|
# Build ZIP from mixed sources: file, directory, and wildcard mask
|
|
222
|
-
gulp-cli ingest zip-create /evidence/evidence.zip /forensic/host1/*.evtx /forensic/host2 /forensic/notes.txt --overwrite
|
|
222
|
+
gulp-cli ingest zip-create /evidence/evidence.zip /forensic/host1/*.evtx /forensic/host2 /forensic/notes.txt --no-overwrite
|
|
223
223
|
|
|
224
224
|
# Use environment variables and ~ in source expressions and output path
|
|
225
225
|
export CASE_ROOT=~/cases/incident-001
|
|
226
|
-
gulp-cli ingest zip-create '$CASE_ROOT/evidence.zip' '$CASE_ROOT/windows/*.evtx' '$CASE_ROOT/network/*' --overwrite
|
|
226
|
+
gulp-cli ingest zip-create '$CASE_ROOT/evidence.zip' '$CASE_ROOT/windows/*.evtx' '$CASE_ROOT/network/*' --no-overwrite
|
|
227
227
|
|
|
228
228
|
# Build ZIP from a text file (one path expression per line)
|
|
229
229
|
cat > /tmp/evidence_paths.txt <<'EOF'
|
|
@@ -613,6 +613,16 @@ done
|
|
|
613
613
|
|
|
614
614
|
## Enrichment & Tagging Workflows
|
|
615
615
|
|
|
616
|
+
### use whois to enrich IP addresses with domain information
|
|
617
|
+
|
|
618
|
+
```bash
|
|
619
|
+
gulp-cli enrich documents incident-001 \
|
|
620
|
+
--plugin enrich_whois \
|
|
621
|
+
--flt '{"operation_ids":["network"]}' \
|
|
622
|
+
--fields '{"ip.destination": null}' \
|
|
623
|
+
--wait
|
|
624
|
+
```
|
|
625
|
+
|
|
616
626
|
### Tag Suspicious Events
|
|
617
627
|
|
|
618
628
|
```bash
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '1.1.
|
|
22
|
-
__version_tuple__ = version_tuple = (1, 1,
|
|
21
|
+
__version__ = version = '1.1.6'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 1, 6)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g8d034cc8b'
|
|
@@ -7,6 +7,7 @@ from gulp_cli.commands.auth import app as auth_app
|
|
|
7
7
|
from gulp_cli.commands.collab import app as collab_app
|
|
8
8
|
from gulp_cli.commands.context import app as context_app
|
|
9
9
|
from gulp_cli.commands.db import app as db_app
|
|
10
|
+
from gulp_cli.commands.enrich import app as enrich_app
|
|
10
11
|
from gulp_cli.commands.enhance_map import app as enhance_map_app
|
|
11
12
|
from gulp_cli.commands.glyph import app as glyph_app
|
|
12
13
|
from gulp_cli.commands.ingest import app as ingest_app
|
|
@@ -14,12 +15,12 @@ from gulp_cli.commands.mapping import app as mapping_app
|
|
|
14
15
|
from gulp_cli.commands.operations import app as operation_app
|
|
15
16
|
from gulp_cli.commands.plugin import app as plugin_app
|
|
16
17
|
from gulp_cli.commands.query import app as query_app
|
|
18
|
+
from gulp_cli.commands.utility import app as utility_app
|
|
17
19
|
from gulp_cli.commands.source import app as source_app
|
|
18
20
|
from gulp_cli.commands.stats import app as stats_app
|
|
19
21
|
from gulp_cli.commands.storage import app as storage_app
|
|
20
22
|
from gulp_cli.commands.user_group import app as user_group_app
|
|
21
23
|
from gulp_cli.commands.users import app as user_app
|
|
22
|
-
from gulp_sdk.client import GulpClient
|
|
23
24
|
|
|
24
25
|
from gulp_cli.config import (
|
|
25
26
|
set_runtime_as_user,
|
|
@@ -27,28 +28,21 @@ from gulp_cli.config import (
|
|
|
27
28
|
set_runtime_verbose,
|
|
28
29
|
)
|
|
29
30
|
from gulp_cli.extensions import load_extensions
|
|
30
|
-
|
|
31
|
-
app = typer.Typer(
|
|
32
|
-
no_args_is_help=True,
|
|
33
|
-
help="Modern CLI for gULP, powered by gulp-sdk",
|
|
34
|
-
)
|
|
31
|
+
from gulp_cli.version import print_version
|
|
35
32
|
|
|
36
33
|
|
|
37
34
|
def _show_versions(value: bool) -> None:
|
|
38
35
|
if not value:
|
|
39
36
|
return
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
sdk_version = GulpClient("http://localhost:8080").version()
|
|
44
|
-
print(
|
|
45
|
-
f"gulp-cli version: {__version__} ({__commit_id__})"
|
|
46
|
-
if __commit_id__
|
|
47
|
-
else __version__
|
|
48
|
-
)
|
|
49
|
-
print(f"gulp-sdk version: {sdk_version}")
|
|
38
|
+
print_version()
|
|
50
39
|
raise typer.Exit()
|
|
51
40
|
|
|
41
|
+
app = typer.Typer(
|
|
42
|
+
no_args_is_help=True,
|
|
43
|
+
help="Modern CLI for gULP, powered by gulp-sdk",
|
|
44
|
+
)
|
|
45
|
+
|
|
52
46
|
|
|
53
47
|
@app.callback(invoke_without_command=True)
|
|
54
48
|
def main(
|
|
@@ -73,7 +67,7 @@ def main(
|
|
|
73
67
|
"--version",
|
|
74
68
|
callback=_show_versions,
|
|
75
69
|
is_eager=True,
|
|
76
|
-
help="Show CLI
|
|
70
|
+
help="Show the CLI version and exit.",
|
|
77
71
|
),
|
|
78
72
|
) -> None:
|
|
79
73
|
set_runtime_config_dir(config_dir)
|
|
@@ -93,11 +87,13 @@ app.add_typer(stats_app, name="stats")
|
|
|
93
87
|
app.add_typer(storage_app, name="storage")
|
|
94
88
|
app.add_typer(collab_app, name="collab")
|
|
95
89
|
app.add_typer(db_app, name="db")
|
|
90
|
+
app.add_typer(enrich_app, name="enrich")
|
|
96
91
|
app.add_typer(acl_app, name="acl")
|
|
97
92
|
app.add_typer(plugin_app, name="plugin")
|
|
98
93
|
app.add_typer(mapping_app, name="mapping")
|
|
99
94
|
app.add_typer(enhance_map_app, name="enhance-map")
|
|
100
95
|
app.add_typer(glyph_app, name="glyph")
|
|
96
|
+
app.add_typer(utility_app, name="utility")
|
|
101
97
|
|
|
102
98
|
load_extensions(
|
|
103
99
|
app,
|
|
@@ -114,10 +110,12 @@ load_extensions(
|
|
|
114
110
|
"storage": storage_app,
|
|
115
111
|
"collab": collab_app,
|
|
116
112
|
"db": db_app,
|
|
113
|
+
"enrich": enrich_app,
|
|
117
114
|
"acl": acl_app,
|
|
118
115
|
"plugin": plugin_app,
|
|
119
116
|
"mapping": mapping_app,
|
|
120
117
|
"enhance-map": enhance_map_app,
|
|
121
118
|
"glyph": glyph_app,
|
|
119
|
+
"utility": utility_app,
|
|
122
120
|
},
|
|
123
121
|
)
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from gulp_cli.client import get_client
|
|
9
|
+
from gulp_cli.output import print_result
|
|
10
|
+
from gulp_cli.utils import comma_split, parse_json_option
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(help="Enrichment and tagging commands")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@app.command("tag")
|
|
16
|
+
def enrich_tag(
|
|
17
|
+
operation_id: str,
|
|
18
|
+
tags: list[str] = typer.Option(..., "--tag", help="Tag to add (repeatable)"),
|
|
19
|
+
flt: str | None = typer.Option(
|
|
20
|
+
None, "--flt", help="JSON object for GulpQueryFilter"
|
|
21
|
+
),
|
|
22
|
+
wait: bool = typer.Option(False, "--wait", help="Wait for the async job to finish"),
|
|
23
|
+
) -> None:
|
|
24
|
+
"""Add tags to documents matching a filter."""
|
|
25
|
+
|
|
26
|
+
async def _run() -> None:
|
|
27
|
+
flt_parsed = parse_json_option(flt, field_name="flt")
|
|
28
|
+
async with get_client() as client:
|
|
29
|
+
result = await client.enrich.tag_documents(
|
|
30
|
+
operation_id=operation_id,
|
|
31
|
+
tags=tags,
|
|
32
|
+
flt=flt_parsed,
|
|
33
|
+
wait=wait,
|
|
34
|
+
)
|
|
35
|
+
print_result(result)
|
|
36
|
+
|
|
37
|
+
asyncio.run(_run())
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@app.command("untag")
|
|
41
|
+
def enrich_untag(
|
|
42
|
+
operation_id: str,
|
|
43
|
+
tags: list[str] = typer.Option(..., "--tag", help="Tag to remove (repeatable)"),
|
|
44
|
+
flt: str | None = typer.Option(
|
|
45
|
+
None, "--flt", help="JSON object for GulpQueryFilter"
|
|
46
|
+
),
|
|
47
|
+
wait: bool = typer.Option(False, "--wait", help="Wait for the async job to finish"),
|
|
48
|
+
) -> None:
|
|
49
|
+
"""Remove tags from documents matching a filter."""
|
|
50
|
+
|
|
51
|
+
async def _run() -> None:
|
|
52
|
+
flt_parsed = parse_json_option(flt, field_name="flt")
|
|
53
|
+
async with get_client() as client:
|
|
54
|
+
result = await client.enrich.untag_documents(
|
|
55
|
+
operation_id=operation_id,
|
|
56
|
+
tags=tags,
|
|
57
|
+
flt=flt_parsed,
|
|
58
|
+
wait=wait,
|
|
59
|
+
)
|
|
60
|
+
print_result(result)
|
|
61
|
+
|
|
62
|
+
asyncio.run(_run())
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@app.command("update")
|
|
66
|
+
def enrich_update(
|
|
67
|
+
operation_id: str,
|
|
68
|
+
fields: str = typer.Option(..., "--fields", help="JSON object of field updates"),
|
|
69
|
+
flt: str | None = typer.Option(
|
|
70
|
+
None, "--flt", help="JSON object for GulpQueryFilter"
|
|
71
|
+
),
|
|
72
|
+
wait: bool = typer.Option(False, "--wait", help="Wait for the async job to finish"),
|
|
73
|
+
) -> None:
|
|
74
|
+
"""Update fields on documents matching a filter."""
|
|
75
|
+
|
|
76
|
+
async def _run() -> None:
|
|
77
|
+
fields_parsed = parse_json_option(fields, field_name="fields")
|
|
78
|
+
if fields_parsed is None:
|
|
79
|
+
raise typer.BadParameter("--fields is required")
|
|
80
|
+
flt_parsed = parse_json_option(flt, field_name="flt")
|
|
81
|
+
async with get_client() as client:
|
|
82
|
+
result = await client.enrich.update_documents(
|
|
83
|
+
operation_id=operation_id,
|
|
84
|
+
fields=fields_parsed,
|
|
85
|
+
flt=flt_parsed,
|
|
86
|
+
wait=wait,
|
|
87
|
+
)
|
|
88
|
+
print_result(result)
|
|
89
|
+
|
|
90
|
+
asyncio.run(_run())
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@app.command("remove")
|
|
94
|
+
def enrich_remove(
|
|
95
|
+
operation_id: str,
|
|
96
|
+
fields: str = typer.Option(
|
|
97
|
+
..., "--fields", help="Comma-separated field names to remove"
|
|
98
|
+
),
|
|
99
|
+
flt: str | None = typer.Option(
|
|
100
|
+
None, "--flt", help="JSON object for GulpQueryFilter"
|
|
101
|
+
),
|
|
102
|
+
wait: bool = typer.Option(False, "--wait", help="Wait for the async job to finish"),
|
|
103
|
+
) -> None:
|
|
104
|
+
"""Remove enrichment markers or specific fields from documents matching a filter."""
|
|
105
|
+
|
|
106
|
+
async def _run() -> None:
|
|
107
|
+
flt_parsed = parse_json_option(flt, field_name="flt")
|
|
108
|
+
field_names = comma_split(fields)
|
|
109
|
+
async with get_client() as client:
|
|
110
|
+
result = await client.enrich.enrich_remove(
|
|
111
|
+
operation_id=operation_id,
|
|
112
|
+
fields=field_names or None,
|
|
113
|
+
flt=flt_parsed,
|
|
114
|
+
wait=wait,
|
|
115
|
+
)
|
|
116
|
+
print_result(result)
|
|
117
|
+
|
|
118
|
+
asyncio.run(_run())
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@app.command("documents")
|
|
122
|
+
def enrich_documents(
|
|
123
|
+
operation_id: str,
|
|
124
|
+
plugin: str = typer.Option(..., "--plugin", help="Enrichment plugin name"),
|
|
125
|
+
fields: str | None = typer.Option(
|
|
126
|
+
None, "--fields", help="JSON object of fields to enrich"
|
|
127
|
+
),
|
|
128
|
+
flt: str | None = typer.Option(
|
|
129
|
+
None, "--flt", help="JSON object for GulpQueryFilter"
|
|
130
|
+
),
|
|
131
|
+
plugin_params: str | None = typer.Option(
|
|
132
|
+
None, "--plugin-params", help="JSON object for GulpPluginParameters"
|
|
133
|
+
),
|
|
134
|
+
wait: bool = typer.Option(False, "--wait", help="Wait for the async job to finish"),
|
|
135
|
+
) -> None:
|
|
136
|
+
"""Run an enrichment plugin on documents matching a filter."""
|
|
137
|
+
|
|
138
|
+
async def _run() -> None:
|
|
139
|
+
fields_parsed = parse_json_option(fields, field_name="fields")
|
|
140
|
+
flt_parsed = parse_json_option(flt, field_name="flt")
|
|
141
|
+
plugin_params_parsed = parse_json_option(
|
|
142
|
+
plugin_params, field_name="plugin-params"
|
|
143
|
+
)
|
|
144
|
+
async with get_client() as client:
|
|
145
|
+
result = await client.enrich.enrich_documents(
|
|
146
|
+
operation_id=operation_id,
|
|
147
|
+
plugin=plugin,
|
|
148
|
+
fields=fields_parsed,
|
|
149
|
+
flt=flt_parsed,
|
|
150
|
+
plugin_params=plugin_params_parsed,
|
|
151
|
+
wait=wait,
|
|
152
|
+
)
|
|
153
|
+
print_result(result)
|
|
154
|
+
|
|
155
|
+
asyncio.run(_run())
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@app.command("single-id")
|
|
159
|
+
def enrich_single_id(
|
|
160
|
+
operation_id: str,
|
|
161
|
+
doc_id: str,
|
|
162
|
+
plugin: str = typer.Option(..., "--plugin", help="Enrichment plugin name"),
|
|
163
|
+
fields: str | None = typer.Option(
|
|
164
|
+
None, "--fields", help="JSON object of fields to enrich"
|
|
165
|
+
),
|
|
166
|
+
plugin_params: str | None = typer.Option(
|
|
167
|
+
None, "--plugin-params", help="JSON object for GulpPluginParameters"
|
|
168
|
+
),
|
|
169
|
+
) -> None:
|
|
170
|
+
"""Enrich a single document by OpenSearch document ID."""
|
|
171
|
+
|
|
172
|
+
async def _run() -> None:
|
|
173
|
+
fields_parsed = parse_json_option(fields, field_name="fields")
|
|
174
|
+
plugin_params_parsed = parse_json_option(
|
|
175
|
+
plugin_params, field_name="plugin-params"
|
|
176
|
+
)
|
|
177
|
+
async with get_client() as client:
|
|
178
|
+
result = await client.enrich.enrich_single_id(
|
|
179
|
+
operation_id=operation_id,
|
|
180
|
+
doc_id=doc_id,
|
|
181
|
+
plugin=plugin,
|
|
182
|
+
fields=fields_parsed,
|
|
183
|
+
plugin_params=plugin_params_parsed,
|
|
184
|
+
)
|
|
185
|
+
print_result(result)
|
|
186
|
+
|
|
187
|
+
asyncio.run(_run())
|