dataspring-cli 0.4.0__tar.gz → 0.4.1__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.
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/PKG-INFO +1 -1
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/auth.py +4 -1
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/bundled_manifest.py +1 -1
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/contract.py +16 -2
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/generated.py +12 -2
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/main.py +31 -3
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/pyproject.toml +1 -1
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/skills/dataspring-author/SKILL.md +7 -1
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/.gitignore +0 -0
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/README.md +0 -0
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/__init__.py +0 -0
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/output.py +0 -0
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/runtime.py +0 -0
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/skills_commands.py +0 -0
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/skilltree.py +0 -0
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/upgrade.py +0 -0
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/version.py +0 -0
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/hatch_build.py +0 -0
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/settings.py +0 -0
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/skills/dataspring-consume/SKILL.md +0 -0
- {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/skills/dataspring-correct/SKILL.md +0 -0
|
@@ -145,8 +145,11 @@ class CLIAuthManager:
|
|
|
145
145
|
if r.status_code != 200:
|
|
146
146
|
raise AuthenticationError(f"Could not start the sign-in (HTTP {r.status_code})")
|
|
147
147
|
start = r.json()
|
|
148
|
+
# flush: an agent runs this with stdout redirected, where Python
|
|
149
|
+
# buffers it; unflushed, the code appeared only after the CLI exited,
|
|
150
|
+
# too late for anyone to approve it (tick 373).
|
|
148
151
|
print(f"To sign in, open {start['verification_uri']} and enter the code {start['user_code']}")
|
|
149
|
-
print(f" (or open {start['verification_uri_complete']})")
|
|
152
|
+
print(f" (or open {start['verification_uri_complete']})", flush=True)
|
|
150
153
|
if open_browser:
|
|
151
154
|
try:
|
|
152
155
|
webbrowser.open(start["verification_uri_complete"])
|
|
@@ -6,7 +6,7 @@ the server's to say whether the server has operations this CLI does not
|
|
|
6
6
|
know. tests/test_skills_surface.py asserts it is current.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
MANIFEST_HASH = "
|
|
9
|
+
MANIFEST_HASH = "eacbe5d755f415a814b50b568eb105ff60693b92fe1ce44d30e39f71670d15d0"
|
|
10
10
|
TOOL_KEYS = (
|
|
11
11
|
"business_context_edit",
|
|
12
12
|
"semantic_model_edit",
|
|
@@ -656,7 +656,11 @@ class RunPipelineAction(DispatchParams):
|
|
|
656
656
|
pipeline: str = Field(description="Pipeline name (a deployed pipelines/<name>.yaml)")
|
|
657
657
|
window: RunWindowParams | None = Field(
|
|
658
658
|
default=None,
|
|
659
|
-
description=
|
|
659
|
+
description=(
|
|
660
|
+
"A date window to load instead of the pipeline's own; wider than backfill_chunk becomes a chunked backfill. "
|
|
661
|
+
"Both ends are inclusive days, and so is every chunk; a resource whose API reads its end bound as exclusive "
|
|
662
|
+
"declares `window.end: exclusive` in the pipeline and is sent the day after each chunk's last day"
|
|
663
|
+
),
|
|
660
664
|
)
|
|
661
665
|
cursor: RunCursorParams | None = Field(default=None, description="{from: <value>}: a cursor to re-pull from")
|
|
662
666
|
backfill_chunk: str | None = Field(
|
|
@@ -674,8 +678,18 @@ class ResetPipelineAction(DispatchParams):
|
|
|
674
678
|
)
|
|
675
679
|
|
|
676
680
|
|
|
681
|
+
class CancelRunAction(DispatchParams):
|
|
682
|
+
action: Literal["cancel"]
|
|
683
|
+
run_id: str = Field(
|
|
684
|
+
description=(
|
|
685
|
+
"The queued or running run to stop, or a backfill's parent run (its running child "
|
|
686
|
+
"is stopped and no further chunk starts); ids from dataspring://datacore/runs"
|
|
687
|
+
),
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
|
|
677
691
|
DatacoreRunAction = Annotated[
|
|
678
|
-
CheckDatacoreAction | DeployDatacoreAction | RunPipelineAction | ResetPipelineAction,
|
|
692
|
+
CheckDatacoreAction | DeployDatacoreAction | RunPipelineAction | ResetPipelineAction | CancelRunAction,
|
|
679
693
|
Field(discriminator="action"),
|
|
680
694
|
]
|
|
681
695
|
|
|
@@ -69,7 +69,7 @@ VERBS: dict[str, tuple[str, ...]] = {
|
|
|
69
69
|
'warehouse_edit': ('activate', 'add_external', 'remove'),
|
|
70
70
|
'secret_edit': ('set', 'reveal'),
|
|
71
71
|
'datacore_edit': ('apply', 'patch', 'delete'),
|
|
72
|
-
'datacore_run': ('check', 'deploy', 'run', 'reset'),
|
|
72
|
+
'datacore_run': ('check', 'deploy', 'run', 'reset', 'cancel'),
|
|
73
73
|
'tenant_edit': ('create', 'suspend', 'resume', 'offboard'),
|
|
74
74
|
'board_edit': ('apply', 'patch', 'delete', 'convert_dashboard'),
|
|
75
75
|
'watch_edit': ('apply', 'patch', 'delete', 'rotate_secret'),
|
|
@@ -885,7 +885,7 @@ def datacore_run__deploy(
|
|
|
885
885
|
@datacore_run.command('run', short_help='`datacore_run` run.')
|
|
886
886
|
def datacore_run__run(
|
|
887
887
|
pipeline: Annotated[str, typer.Option('--pipeline', help='Pipeline name (a deployed pipelines/<name>.yaml)')],
|
|
888
|
-
window: Annotated[Optional[str], typer.Option('--window-json', help="A date window to load instead of the pipeline's own; wider than backfill_chunk becomes a chunked backfill (JSON object with keys: start, end, or @path to a .json/.yaml file)")] = None,
|
|
888
|
+
window: Annotated[Optional[str], typer.Option('--window-json', help="A date window to load instead of the pipeline's own; wider than backfill_chunk becomes a chunked backfill. Both ends are inclusive days, and so is every chunk; a resource whose API reads its end bound as exclusive declares `window.end: exclusive` in the pipeline and is sent the day after each chunk's last day (JSON object with keys: start, end, or @path to a .json/.yaml file)")] = None,
|
|
889
889
|
cursor: Annotated[Optional[str], typer.Option('--cursor-json', help='{from: <value>}: a cursor to re-pull from (JSON object with keys: from, or @path to a .json/.yaml file)')] = None,
|
|
890
890
|
backfill_chunk: Annotated[Optional[str], typer.Option('--backfill-chunk', help='Chunk size for a windowed run: 7d, 2w or 1M (calendar months, the default)')] = None,
|
|
891
891
|
from_file: FromFile = None,
|
|
@@ -906,6 +906,16 @@ def datacore_run__reset(
|
|
|
906
906
|
run_tool('datacore_run', contract.DatacoreRunParams, 'reset', {'pipeline': pipeline, 'approve': approve}, from_file=from_file, output=output)
|
|
907
907
|
|
|
908
908
|
|
|
909
|
+
@datacore_run.command('cancel', short_help='`datacore_run` cancel.')
|
|
910
|
+
def datacore_run__cancel(
|
|
911
|
+
run_id: Annotated[str, typer.Option('--run-id', help="The queued or running run to stop, or a backfill's parent run (its running child is stopped and no further chunk starts); ids from dataspring://datacore/runs")],
|
|
912
|
+
from_file: FromFile = None,
|
|
913
|
+
output: Output = None,
|
|
914
|
+
) -> None:
|
|
915
|
+
"""`datacore_run` cancel."""
|
|
916
|
+
run_tool('datacore_run', contract.DatacoreRunParams, 'cancel', {'run_id': run_id}, from_file=from_file, output=output)
|
|
917
|
+
|
|
918
|
+
|
|
909
919
|
# ---------------------------------------------------------------------------
|
|
910
920
|
# tenant_edit
|
|
911
921
|
# ---------------------------------------------------------------------------
|
|
@@ -2552,11 +2552,15 @@ def _print_check_report(run: dict) -> None:
|
|
|
2552
2552
|
mark = "ok " if entry.get("ok") else "ERR"
|
|
2553
2553
|
typer.echo(f" connection {mark} {entry['name']}" + ("" if entry.get("ok") else f": {entry.get('error')}"))
|
|
2554
2554
|
dbt = report.get("dbt") or {}
|
|
2555
|
-
if dbt:
|
|
2555
|
+
if dbt.get("status") == "skipped" and dbt.get("reason"):
|
|
2556
|
+
typer.echo(f" dbt skipped: {dbt['reason']}")
|
|
2557
|
+
elif dbt:
|
|
2556
2558
|
typer.echo(
|
|
2557
2559
|
f" dbt {dbt.get('status')}: {dbt.get('models', 0)} models, {dbt.get('tests', 0)} tests, "
|
|
2558
2560
|
f"{dbt.get('seeds', 0)} seeds" + (f" -> {dbt.get('target')}" if dbt.get("target") else "")
|
|
2559
2561
|
)
|
|
2562
|
+
if dbt.get("error"):
|
|
2563
|
+
typer.echo(f" {dbt['error']}")
|
|
2560
2564
|
for failure in dbt.get("failures") or []:
|
|
2561
2565
|
typer.echo(f" FAIL {failure.get('node')}: {failure.get('message')}")
|
|
2562
2566
|
for warning in dbt.get("warnings") or []:
|
|
@@ -2579,7 +2583,7 @@ def _poll_run(run_id: str, token: str, *, interval: float = 10.0, timeout: float
|
|
|
2579
2583
|
if run.get("status") != last:
|
|
2580
2584
|
last = run.get("status")
|
|
2581
2585
|
print_info(f"run {run_id}: {last}")
|
|
2582
|
-
if last in ("succeeded", "failed"):
|
|
2586
|
+
if last in ("succeeded", "failed", "cancelled"):
|
|
2583
2587
|
return run
|
|
2584
2588
|
if time.monotonic() > deadline:
|
|
2585
2589
|
print_error(f"run {run_id} still {last} after {int(timeout)}s")
|
|
@@ -2697,7 +2701,7 @@ def datacore_run_cmd(
|
|
|
2697
2701
|
except Exception as e:
|
|
2698
2702
|
fail(e, role_action="read the run")
|
|
2699
2703
|
if run.get("status") != "succeeded":
|
|
2700
|
-
print_error(f"run {run['id']}
|
|
2704
|
+
print_error(f"run {run['id']} {run.get('status')}: {(run.get('error') or {}).get('message')}")
|
|
2701
2705
|
raise typer.Exit(1)
|
|
2702
2706
|
|
|
2703
2707
|
|
|
@@ -2723,6 +2727,30 @@ def datacore_reset_cmd(
|
|
|
2723
2727
|
print_success(f"reset {pipeline}: dropped {', '.join(result.get('tables') or []) or 'no tables'}")
|
|
2724
2728
|
|
|
2725
2729
|
|
|
2730
|
+
@datacore_app.command("cancel")
|
|
2731
|
+
def datacore_cancel_cmd(
|
|
2732
|
+
run_id: Annotated[str, typer.Argument(help="The run, or a backfill's parent run, to stop")],
|
|
2733
|
+
):
|
|
2734
|
+
"""Stop a queued or running run, or a backfill (admin).
|
|
2735
|
+
|
|
2736
|
+
A running run's Cloud Run execution is cancelled; a backfill's parent
|
|
2737
|
+
and its current child are cancelled and no further chunk starts. The
|
|
2738
|
+
pipeline's schedule fires again at its next slot.
|
|
2739
|
+
|
|
2740
|
+
Example:
|
|
2741
|
+
dataspring datacore cancel r_01M4TV7V8K2MGX9VY9XV9S0HFR
|
|
2742
|
+
"""
|
|
2743
|
+
require_auth()
|
|
2744
|
+
token = auth.get_access_token()
|
|
2745
|
+
try:
|
|
2746
|
+
result = call_tool("datacore_run", "cancel", {"run_id": run_id}, token=token)
|
|
2747
|
+
except Exception as e:
|
|
2748
|
+
fail(e, role_action="cancel a run")
|
|
2749
|
+
others = [r for r in result.get("cancelled") or [] if r != run_id]
|
|
2750
|
+
also = f" (also {', '.join(others)})" if others else ""
|
|
2751
|
+
print_success(f"cancelled {result.get('kind', 'run')} {run_id}{also}")
|
|
2752
|
+
|
|
2753
|
+
|
|
2726
2754
|
@datacore_app.command("status")
|
|
2727
2755
|
def datacore_status(
|
|
2728
2756
|
format: Annotated[OutputFormat, typer.Option("--format", "-f", help="Output format")] = "table",
|
|
@@ -467,7 +467,7 @@ MCP tool `datacore_run`; `POST /api/dispatch/datacore_run`. The datacore's check
|
|
|
467
467
|
| Parameter | Type | Required | Description |
|
|
468
468
|
|---|---|---|---|
|
|
469
469
|
| `pipeline` | string | yes | Pipeline name (a deployed pipelines/<name>.yaml) |
|
|
470
|
-
| `window` | RunWindowParams | no | A date window to load instead of the pipeline's own; wider than backfill_chunk becomes a chunked backfill |
|
|
470
|
+
| `window` | RunWindowParams | no | A date window to load instead of the pipeline's own; wider than backfill_chunk becomes a chunked backfill. Both ends are inclusive days, and so is every chunk; a resource whose API reads its end bound as exclusive declares `window.end: exclusive` in the pipeline and is sent the day after each chunk's last day |
|
|
471
471
|
| `cursor` | RunCursorParams | no | {from: <value>}: a cursor to re-pull from |
|
|
472
472
|
| `backfill_chunk` | string | no | Chunk size for a windowed run: 7d, 2w or 1M (calendar months, the default) |
|
|
473
473
|
|
|
@@ -477,4 +477,10 @@ MCP tool `datacore_run`; `POST /api/dispatch/datacore_run`. The datacore's check
|
|
|
477
477
|
|---|---|---|---|
|
|
478
478
|
| `pipeline` | string | yes | The pipeline whose dlt state and raw tables to remove |
|
|
479
479
|
| `approve` | boolean | no, default `False` | Must be true: the reset is irreversible. Ask the user first |
|
|
480
|
+
|
|
481
|
+
**`cancel`** —
|
|
482
|
+
|
|
483
|
+
| Parameter | Type | Required | Description |
|
|
484
|
+
|---|---|---|---|
|
|
485
|
+
| `run_id` | string | yes | The queued or running run to stop, or a backfill's parent run (its running child is stopped and no further chunk starts); ids from dataspring://datacore/runs |
|
|
480
486
|
<!-- /dispatch-params -->
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|