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.
Files changed (21) hide show
  1. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/PKG-INFO +1 -1
  2. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/auth.py +4 -1
  3. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/bundled_manifest.py +1 -1
  4. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/contract.py +16 -2
  5. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/generated.py +12 -2
  6. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/main.py +31 -3
  7. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/pyproject.toml +1 -1
  8. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/skills/dataspring-author/SKILL.md +7 -1
  9. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/.gitignore +0 -0
  10. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/README.md +0 -0
  11. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/__init__.py +0 -0
  12. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/output.py +0 -0
  13. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/runtime.py +0 -0
  14. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/skills_commands.py +0 -0
  15. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/skilltree.py +0 -0
  16. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/upgrade.py +0 -0
  17. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/cli/version.py +0 -0
  18. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/hatch_build.py +0 -0
  19. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/settings.py +0 -0
  20. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/skills/dataspring-consume/SKILL.md +0 -0
  21. {dataspring_cli-0.4.0 → dataspring_cli-0.4.1}/skills/dataspring-correct/SKILL.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dataspring-cli
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: DataSpring CLI - Query metrics and manage dashboards from the terminal
5
5
  Project-URL: Homepage, https://dataspring.app
6
6
  Project-URL: Documentation, https://dataspring.app/docs
@@ -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 = "386b42bdef87f3cc5aabaa69e1aec1b327b3a2f3f5dc5559eda1cf91a40ae48c"
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="A date window to load instead of the pipeline's own; wider than backfill_chunk becomes a chunked backfill",
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']} failed: {(run.get('error') or {}).get('message')}")
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",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dataspring-cli"
3
- version = "0.4.0"
3
+ version = "0.4.1"
4
4
  description = "DataSpring CLI - Query metrics and manage dashboards from the terminal"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -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