dirigent-cli 0.20.0__tar.gz → 0.21.0__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 (37) hide show
  1. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/PKG-INFO +9 -9
  2. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/pyproject.toml +9 -9
  3. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/pyproject.toml.orig +9 -9
  4. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/commands.py +3 -1
  5. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/local.py +4 -3
  6. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/main.py +3 -2
  7. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/seeding.py +2 -1
  8. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/triggers.py +70 -3
  9. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/LICENSE +0 -0
  10. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/README.md +0 -0
  11. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/__init__.py +0 -0
  12. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/aliases.py +0 -0
  13. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/context.py +0 -0
  14. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/explain.py +0 -0
  15. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/formatters.py +0 -0
  16. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/graph.py +0 -0
  17. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/health.py +0 -0
  18. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/init_form.py +0 -0
  19. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/messages.py +0 -0
  20. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/output.py +0 -0
  21. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/params.py +0 -0
  22. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/profiles.py +0 -0
  23. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/project.py +0 -0
  24. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/py.typed +0 -0
  25. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/reaper.py +0 -0
  26. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/scaffold.py +0 -0
  27. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/schemas.py +0 -0
  28. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/sources.py +0 -0
  29. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/starters.py +0 -0
  30. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/stream.py +0 -0
  31. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/summaries.py +0 -0
  32. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/templates/pack/README.md.tmpl +0 -0
  33. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/templates/pack/__init__.py.tmpl +0 -0
  34. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/templates/pack/operator.py.tmpl +0 -0
  35. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/templates/pack/pyproject.toml.tmpl +0 -0
  36. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/templates/pack/test_plugin.py.tmpl +0 -0
  37. {dirigent_cli-0.20.0 → dirigent_cli-0.21.0}/src/dirigent_cli/timing.py +0 -0
@@ -1,19 +1,19 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirigent-cli
3
- Version: 0.20.0
3
+ Version: 0.21.0
4
4
  Summary: The dirigent command line interface (dirigent / dg).
5
5
  License-Expression: LicenseRef-Proprietary
6
6
  License-File: LICENSE
7
7
  Classifier: Programming Language :: Python :: 3
8
8
  Classifier: Programming Language :: Python :: 3.13
9
- Requires-Dist: dirigent-block-execute==0.20.0
10
- Requires-Dist: dirigent-blocks==0.20.0
11
- Requires-Dist: dirigent-client==0.20.0
12
- Requires-Dist: dirigent-common==0.20.0
13
- Requires-Dist: dirigent-core==0.20.0
14
- Requires-Dist: dirigent-examples==0.20.0
15
- Requires-Dist: dirigent-plugin==0.20.0
16
- Requires-Dist: dirigent-server==0.20.0
9
+ Requires-Dist: dirigent-block-execute==0.21.0
10
+ Requires-Dist: dirigent-blocks==0.21.0
11
+ Requires-Dist: dirigent-client==0.21.0
12
+ Requires-Dist: dirigent-common==0.21.0
13
+ Requires-Dist: dirigent-core==0.21.0
14
+ Requires-Dist: dirigent-examples==0.21.0
15
+ Requires-Dist: dirigent-plugin==0.21.0
16
+ Requires-Dist: dirigent-server==0.21.0
17
17
  Requires-Dist: httpx2>=2.12.0
18
18
  Requires-Dist: python-dotenv>=1.1.0
19
19
  Requires-Dist: pyyaml>=6.0.3
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dirigent-cli"
3
- version = "0.20.0"
3
+ version = "0.21.0"
4
4
  description = "The dirigent command line interface (dirigent / dg)."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -11,14 +11,14 @@ classifiers = [
11
11
  "Programming Language :: Python :: 3.13",
12
12
  ]
13
13
  dependencies = [
14
- "dirigent-block-execute==0.20.0",
15
- "dirigent-blocks==0.20.0",
16
- "dirigent-client==0.20.0",
17
- "dirigent-common==0.20.0",
18
- "dirigent-core==0.20.0",
19
- "dirigent-examples==0.20.0",
20
- "dirigent-plugin==0.20.0",
21
- "dirigent-server==0.20.0",
14
+ "dirigent-block-execute==0.21.0",
15
+ "dirigent-blocks==0.21.0",
16
+ "dirigent-client==0.21.0",
17
+ "dirigent-common==0.21.0",
18
+ "dirigent-core==0.21.0",
19
+ "dirigent-examples==0.21.0",
20
+ "dirigent-plugin==0.21.0",
21
+ "dirigent-server==0.21.0",
22
22
  "httpx2>=2.12.0",
23
23
  "python-dotenv>=1.1.0",
24
24
  "pyyaml>=6.0.3",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dirigent-cli"
3
- version = "0.20.0"
3
+ version = "0.21.0"
4
4
  description = "The dirigent command line interface (dirigent / dg)."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -11,14 +11,14 @@ classifiers = [
11
11
  "Programming Language :: Python :: 3.13",
12
12
  ]
13
13
  dependencies = [
14
- "dirigent-block-execute==0.20.0",
15
- "dirigent-blocks==0.20.0",
16
- "dirigent-client==0.20.0",
17
- "dirigent-common==0.20.0",
18
- "dirigent-core==0.20.0",
19
- "dirigent-examples==0.20.0",
20
- "dirigent-plugin==0.20.0",
21
- "dirigent-server==0.20.0",
14
+ "dirigent-block-execute==0.21.0",
15
+ "dirigent-blocks==0.21.0",
16
+ "dirigent-client==0.21.0",
17
+ "dirigent-common==0.21.0",
18
+ "dirigent-core==0.21.0",
19
+ "dirigent-examples==0.21.0",
20
+ "dirigent-plugin==0.21.0",
21
+ "dirigent-server==0.21.0",
22
22
  "httpx2>=2.12.0",
23
23
  "python-dotenv>=1.1.0",
24
24
  "pyyaml>=6.0.3",
@@ -350,7 +350,9 @@ def apply_command(
350
350
  as_code: Annotated[str | None, typer.Option("--as", help="Register under a different code.")] = None,
351
351
  paused: Annotated[
352
352
  bool,
353
- typer.Option("--paused", help="Create the schedules this apply mints paused; existing ones are untouched."),
353
+ typer.Option(
354
+ "--paused", help="Create the schedules and watches this apply mints paused; existing ones are untouched."
355
+ ),
354
356
  ] = False,
355
357
  prune: Annotated[
356
358
  bool,
@@ -645,7 +645,8 @@ async def run_document(
645
645
  async with session_scope(sessions) as session:
646
646
  await _apply_supporting(session, services, extra_definition)
647
647
  async with session_scope(sessions) as session:
648
- applied = await apply_document(session, services, definition)
648
+ # Triggers land paused: the instance runs only the run it was asked for.
649
+ applied = await apply_document(session, services, definition, pause_schedules=True)
649
650
  if not applied.plan.ok:
650
651
  raise LocalError(
651
652
  "the document does not validate against the installed catalog:\n"
@@ -697,8 +698,8 @@ def _parse_supporting(text: str) -> PipelineDefinition:
697
698
 
698
699
 
699
700
  async def _apply_supporting(session: AsyncSession, services: EngineServices, supporting: PipelineDefinition) -> None:
700
- """Apply one document a run depends on, without running it."""
701
- applied = await apply_document(session, services, supporting)
701
+ """Apply one document a run depends on, without running it or arming its triggers."""
702
+ applied = await apply_document(session, services, supporting, pause_schedules=True)
702
703
  if not applied.plan.ok:
703
704
  raise LocalError(
704
705
  f"the supporting document {supporting.code!r} does not validate:\n"
@@ -127,6 +127,7 @@ app.add_typer(commands.schema_app, rich_help_panel=DEFINE_PANEL)
127
127
  app.add_typer(commands.connection_app, rich_help_panel=CONNECT_PANEL)
128
128
  app.add_typer(triggers.schedule_app, rich_help_panel=TRIGGER_PANEL)
129
129
  app.add_typer(triggers.webhook_app, rich_help_panel=TRIGGER_PANEL)
130
+ app.add_typer(triggers.watch_app, rich_help_panel=TRIGGER_PANEL)
130
131
  app.add_typer(triggers.trigger_document_app, rich_help_panel=TRIGGER_PANEL)
131
132
  app.add_typer(triggers.alerts_app, rich_help_panel=TRIGGER_PANEL)
132
133
  app.add_typer(docker_app, rich_help_panel=PROCESS_PANEL)
@@ -911,7 +912,7 @@ def dev(
911
912
  typer.Option(
912
913
  "--seed",
913
914
  help="Apply every dirigent/v1 document under this directory once the API answers, "
914
- "with its schedules paused; name it more than once to seed one directory after another.",
915
+ "with its schedules and watches paused; name it more than once to seed one directory after another.",
915
916
  ),
916
917
  ] = None,
917
918
  seed_installed: Annotated[
@@ -934,7 +935,7 @@ def dev(
934
935
 
935
936
  --seed fills the instance from a directory of documents the moment it answers: the
936
937
  connections a file or a document declares are created first, then every document is
937
- applied with its schedules paused. A document an instance will not store is reported
938
+ applied with its schedules and watches paused. A document an instance will not store is reported
938
939
  and passed over, because a corpus holds those on purpose.
939
940
  """
940
941
  import asyncio
@@ -136,7 +136,7 @@ async def _connection(client: Dirigent, spec: ConnectionSpec) -> str:
136
136
 
137
137
 
138
138
  async def _apply(client: Dirigent, raw: JsonMap, origin: str) -> Record:
139
- """Apply one document with its schedules paused, without the sections it carries."""
139
+ """Apply one document with its schedules and watches paused, without the sections it carries."""
140
140
  document = {name: value for name, value in raw.items() if name not in CARRIED}
141
141
  try:
142
142
  result = await client.pipelines.apply(
@@ -157,6 +157,7 @@ async def _apply(client: Dirigent, raw: JsonMap, origin: str) -> Record:
157
157
  pipeline=result.plan.code,
158
158
  action=result.plan.action.value,
159
159
  schedules_paused=result.triggers.schedules_created,
160
+ watches_paused=result.triggers.watches_created,
160
161
  )
161
162
 
162
163
 
@@ -1,4 +1,4 @@
1
- """The trigger and alerting command groups: schedules, webhooks, and alert rules."""
1
+ """The trigger and alerting command groups: schedules, webhooks, watches, and alert rules."""
2
2
 
3
3
  from pathlib import Path
4
4
  from typing import Annotated, Any, cast
@@ -35,6 +35,11 @@ schedule_app = typer.Typer(
35
35
  name="schedule", help="A pipeline's clocks: cron, interval, or one-time.", no_args_is_help=True
36
36
  )
37
37
  webhook_app = typer.Typer(name="webhook", help="Inbound webhooks and their delivery history.", no_args_is_help=True)
38
+ watch_app = typer.Typer(
39
+ name="watch",
40
+ help="Sensors kept waiting: one run always waiting on each, the next armed on success.",
41
+ no_args_is_help=True,
42
+ )
38
43
  trigger_document_app = typer.Typer(
39
44
  name="trigger-document",
40
45
  help="Documents that declare clocks for a pipeline defined elsewhere.",
@@ -246,6 +251,60 @@ def schedule_delete(
246
251
  emit_fact("schedule.deleted", message="deleted", code=code, pipeline=pipeline)
247
252
 
248
253
 
254
+ @watch_app.command("list")
255
+ def watch_list(
256
+ ctx: typer.Context,
257
+ pipeline: Annotated[str, typer.Argument(help="The pipeline whose watches to list.")],
258
+ ) -> None:
259
+ """List a pipeline's watches, each with the run it has waiting and how its waits are going."""
260
+ with client_for(state_of(ctx)) as dg:
261
+ rows = list(paged(lambda after, size: dg.call(dg.watches.list(pipeline, after=after, limit=size)), None))
262
+ emit_records("watch", rows)
263
+
264
+
265
+ @watch_app.command("show")
266
+ def watch_show(
267
+ ctx: typer.Context,
268
+ pipeline: Annotated[str, typer.Argument(help="The pipeline the watch belongs to.")],
269
+ code: Annotated[str, typer.Argument(help="The watch to show.")],
270
+ ) -> None:
271
+ """Show one watch: the step it waits on, where it left off, and its last error."""
272
+ with client_for(state_of(ctx)) as dg:
273
+ row = dg.call(dg.watches.get(pipeline, code))
274
+ emit_one("watch", row)
275
+
276
+
277
+ @watch_app.command("pause")
278
+ def watch_pause(
279
+ ctx: typer.Context,
280
+ pipeline: Annotated[str, typer.Argument()],
281
+ code: Annotated[str, typer.Argument()],
282
+ ) -> None:
283
+ """Stop a watch, cancelling the run it has waiting, and keep where it left off."""
284
+ with client_for(state_of(ctx)) as dg:
285
+ row = dg.call(dg.watches.pause(pipeline, code))
286
+ emit_fact("watch.paused", message="paused", code=code, pipeline=pipeline, cursor=row.cursor)
287
+
288
+
289
+ @watch_app.command("resume")
290
+ def watch_resume(
291
+ ctx: typer.Context,
292
+ pipeline: Annotated[str, typer.Argument()],
293
+ code: Annotated[str, typer.Argument()],
294
+ ) -> None:
295
+ """Start a watch again, arming a run from where its last success left off."""
296
+ with client_for(state_of(ctx)) as dg:
297
+ row = dg.call(dg.watches.resume(pipeline, code))
298
+ emit_fact(
299
+ "watch.resumed",
300
+ message="resumed",
301
+ code=code,
302
+ pipeline=pipeline,
303
+ waiting_run_id=row.waiting_run_id,
304
+ cursor=row.cursor,
305
+ )
306
+
307
+
249
308
  def _print_token(minted: WebhookTokenOut, *, base_url: str) -> None:
250
309
  """Print a minted token once, with the URL already assembled.
251
310
 
@@ -621,7 +680,7 @@ def trigger_document_show(
621
680
  ctx: typer.Context,
622
681
  code: Annotated[str, typer.Argument(help="The triggers document to read.")],
623
682
  ) -> None:
624
- """Show one triggers document and the schedules and webhooks it owns."""
683
+ """Show one triggers document and the schedules, webhooks and watches it owns."""
625
684
  with client_for(state_of(ctx)) as dg:
626
685
  detail = dg.call(dg.trigger_documents.get(code))
627
686
  if state_of(ctx).json_output:
@@ -631,6 +690,7 @@ def trigger_document_show(
631
690
  console.print(f" digest [dim]{detail.digest}[/]")
632
691
  console.print(f" schedules {', '.join(detail.schedules) or '-'}")
633
692
  console.print(f" webhooks {', '.join(detail.webhooks) or '-'}")
693
+ console.print(f" watches {', '.join(detail.watches) or '-'}")
634
694
  if detail.description:
635
695
  console.print(f"\n{detail.description}")
636
696
 
@@ -643,7 +703,14 @@ def trigger_document_delete(
643
703
  """Remove a triggers document and every schedule and webhook it declared."""
644
704
  with client_for(state_of(ctx)) as dg:
645
705
  dg.call(dg.trigger_documents.delete(code))
646
- emit_fact("trigger_document.deleted", message="deleted", code=code, schedules="deleted", webhooks="deleted")
706
+ emit_fact(
707
+ "trigger_document.deleted",
708
+ message="deleted",
709
+ code=code,
710
+ schedules="deleted",
711
+ webhooks="deleted",
712
+ watches="deleted",
713
+ )
647
714
 
648
715
 
649
716
  @alerts_app.command("retry")
File without changes
File without changes