dirigent-cli 0.16.3__tar.gz → 0.16.5__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.
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/PKG-INFO +10 -8
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/pyproject.toml +12 -8
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/pyproject.toml.orig +12 -8
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/commands.py +27 -3
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/main.py +33 -1
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/output.py +16 -5
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/LICENSE +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/README.md +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/__init__.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/aliases.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/context.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/formatters.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/graph.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/health.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/init_form.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/local.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/params.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/profiles.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/project.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/py.typed +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/reaper.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/scaffold.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/schemas.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/seeding.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/sources.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/starters.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/stream.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/summaries.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/templates/pack/README.md.tmpl +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/templates/pack/__init__.py.tmpl +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/templates/pack/operator.py.tmpl +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/templates/pack/pyproject.toml.tmpl +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/templates/pack/test_plugin.py.tmpl +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/timing.py +0 -0
- {dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/triggers.py +0 -0
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dirigent-cli
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.5
|
|
4
4
|
Summary: The dirigent command line interface (dirigent / dg).
|
|
5
5
|
License-Expression: LicenseRef-Proprietary
|
|
6
6
|
License-File: LICENSE
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Requires-Dist: dirigent-
|
|
10
|
-
Requires-Dist: dirigent-
|
|
11
|
-
Requires-Dist: dirigent-
|
|
12
|
-
Requires-Dist: dirigent-
|
|
13
|
-
Requires-Dist: dirigent-
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
9
|
+
Requires-Dist: dirigent-blocks==0.16.5
|
|
10
|
+
Requires-Dist: dirigent-client==0.16.5
|
|
11
|
+
Requires-Dist: dirigent-common==0.16.5
|
|
12
|
+
Requires-Dist: dirigent-core==0.16.5
|
|
13
|
+
Requires-Dist: dirigent-examples==0.16.5
|
|
14
|
+
Requires-Dist: dirigent-plugin==0.16.5
|
|
15
|
+
Requires-Dist: dirigent-server==0.16.5
|
|
14
16
|
Requires-Dist: httpx2>=2.12.0
|
|
15
17
|
Requires-Dist: python-dotenv>=1.1.0
|
|
16
18
|
Requires-Dist: pyyaml>=6.0.3
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dirigent-cli"
|
|
3
|
-
version = "0.16.
|
|
3
|
+
version = "0.16.5"
|
|
4
4
|
description = "The dirigent command line interface (dirigent / dg)."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
7
7
|
license = "LicenseRef-Proprietary"
|
|
8
8
|
license-files = ["LICENSE"]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Programming Language :: Python :: 3",
|
|
11
|
+
"Programming Language :: Python :: 3.13",
|
|
12
|
+
]
|
|
9
13
|
dependencies = [
|
|
10
|
-
"dirigent-blocks==0.16.
|
|
11
|
-
"dirigent-client==0.16.
|
|
12
|
-
"dirigent-common==0.16.
|
|
13
|
-
"dirigent-core==0.16.
|
|
14
|
-
"dirigent-examples==0.16.
|
|
15
|
-
"dirigent-plugin==0.16.
|
|
16
|
-
"dirigent-server==0.16.
|
|
14
|
+
"dirigent-blocks==0.16.5",
|
|
15
|
+
"dirigent-client==0.16.5",
|
|
16
|
+
"dirigent-common==0.16.5",
|
|
17
|
+
"dirigent-core==0.16.5",
|
|
18
|
+
"dirigent-examples==0.16.5",
|
|
19
|
+
"dirigent-plugin==0.16.5",
|
|
20
|
+
"dirigent-server==0.16.5",
|
|
17
21
|
"httpx2>=2.12.0",
|
|
18
22
|
"python-dotenv>=1.1.0",
|
|
19
23
|
"pyyaml>=6.0.3",
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dirigent-cli"
|
|
3
|
-
version = "0.16.
|
|
3
|
+
version = "0.16.5"
|
|
4
4
|
description = "The dirigent command line interface (dirigent / dg)."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
7
7
|
license = "LicenseRef-Proprietary"
|
|
8
8
|
license-files = ["LICENSE"]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Programming Language :: Python :: 3",
|
|
11
|
+
"Programming Language :: Python :: 3.13",
|
|
12
|
+
]
|
|
9
13
|
dependencies = [
|
|
10
|
-
"dirigent-blocks==0.16.
|
|
11
|
-
"dirigent-client==0.16.
|
|
12
|
-
"dirigent-common==0.16.
|
|
13
|
-
"dirigent-core==0.16.
|
|
14
|
-
"dirigent-examples==0.16.
|
|
15
|
-
"dirigent-plugin==0.16.
|
|
16
|
-
"dirigent-server==0.16.
|
|
14
|
+
"dirigent-blocks==0.16.5",
|
|
15
|
+
"dirigent-client==0.16.5",
|
|
16
|
+
"dirigent-common==0.16.5",
|
|
17
|
+
"dirigent-core==0.16.5",
|
|
18
|
+
"dirigent-examples==0.16.5",
|
|
19
|
+
"dirigent-plugin==0.16.5",
|
|
20
|
+
"dirigent-server==0.16.5",
|
|
17
21
|
"httpx2>=2.12.0",
|
|
18
22
|
"python-dotenv>=1.1.0",
|
|
19
23
|
"pyyaml>=6.0.3",
|
|
@@ -170,6 +170,11 @@ def fail(message: str) -> NoReturn:
|
|
|
170
170
|
raise typer.Exit(code=1)
|
|
171
171
|
|
|
172
172
|
|
|
173
|
+
def at_a_terminal() -> bool:
|
|
174
|
+
"""Report whether somebody is there to answer a prompt."""
|
|
175
|
+
return sys.stdin.isatty()
|
|
176
|
+
|
|
177
|
+
|
|
173
178
|
def ask(label: str, *, hide: bool = False) -> str:
|
|
174
179
|
"""Read one value from the terminal, refusing under --json where nothing can answer.
|
|
175
180
|
|
|
@@ -885,11 +890,21 @@ def parse_params(
|
|
|
885
890
|
schema: dict[str, Any] | None = None,
|
|
886
891
|
files: list[Path] | None = None,
|
|
887
892
|
) -> dict[str, Any]:
|
|
888
|
-
"""Build a run's parameters from files and flags, coerced against the pipeline's schema.
|
|
893
|
+
"""Build a run's parameters from files and flags, coerced against the pipeline's schema.
|
|
894
|
+
|
|
895
|
+
An empty value for a secret field means unset: ``--set api_token=`` leaves the field out
|
|
896
|
+
altogether, so nothing stores a credential that every read afterwards reports as set.
|
|
897
|
+
"""
|
|
889
898
|
try:
|
|
890
|
-
|
|
899
|
+
built = build_params(schema or {}, pairs=pairs or [], files=files or [])
|
|
891
900
|
except ParamError as error:
|
|
892
901
|
fail(str(error))
|
|
902
|
+
properties = cast("dict[str, Any]", (schema or {}).get("properties") or {})
|
|
903
|
+
return {
|
|
904
|
+
name: value
|
|
905
|
+
for name, value in built.items()
|
|
906
|
+
if value != "" or not _is_secret_field(cast("dict[str, Any]", properties.get(name) or {}))
|
|
907
|
+
}
|
|
893
908
|
|
|
894
909
|
|
|
895
910
|
#: What separates the two ends of a ``--window`` value.
|
|
@@ -2224,12 +2239,19 @@ def connection_create(
|
|
|
2224
2239
|
"""Create a connection, prompting for the secret fields without echoing them.
|
|
2225
2240
|
|
|
2226
2241
|
Prompting happens only on a terminal; in a script every value must arrive via ``--set``.
|
|
2242
|
+
An invocation carrying ``--set`` is prompted only for what it cannot do without: a
|
|
2243
|
+
required field it left out. A bare invocation is the interactive form and is offered
|
|
2244
|
+
every secret the kind declares.
|
|
2227
2245
|
"""
|
|
2228
2246
|
with client_for(state_of(ctx)) as dg:
|
|
2229
2247
|
schema = _connection_schema(dg.call(dg.blocks.catalog()), kind_id)
|
|
2230
2248
|
config = parse_params(set_value, schema=schema)
|
|
2231
2249
|
required = set(cast("list[str]", schema.get("required") or []))
|
|
2232
|
-
interactive =
|
|
2250
|
+
interactive = at_a_terminal() and not json_mode()
|
|
2251
|
+
# An invocation that carried --set said what it wanted, so only a required secret is
|
|
2252
|
+
# still asked for there: an optional one it left out is left out. With no --set at
|
|
2253
|
+
# all the command is the form, and every secret is offered.
|
|
2254
|
+
offered = not set_value
|
|
2233
2255
|
for field, body in cast("dict[str, Any]", schema.get("properties") or {}).items():
|
|
2234
2256
|
if field in config:
|
|
2235
2257
|
continue
|
|
@@ -2241,6 +2263,8 @@ def connection_create(
|
|
|
2241
2263
|
if not interactive:
|
|
2242
2264
|
continue
|
|
2243
2265
|
if _is_secret_field(body):
|
|
2266
|
+
if not offered and field not in required:
|
|
2267
|
+
continue
|
|
2244
2268
|
value = typer.prompt(f"{field}", hide_input=True, default="", show_default=False)
|
|
2245
2269
|
if value:
|
|
2246
2270
|
config[field] = value
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import os
|
|
4
4
|
import shutil
|
|
5
5
|
import sys
|
|
6
|
-
from collections.abc import Sequence
|
|
6
|
+
from collections.abc import Callable, Sequence
|
|
7
7
|
from datetime import UTC, datetime, timedelta
|
|
8
8
|
from importlib.metadata import PackageNotFoundError, version
|
|
9
9
|
from pathlib import Path
|
|
@@ -1076,8 +1076,15 @@ async def _dev(
|
|
|
1076
1076
|
install_signal_handlers(worker)
|
|
1077
1077
|
server_config = uvicorn.Config(build_app(), host=host, port=port, log_config=None)
|
|
1078
1078
|
api = uvicorn.Server(server_config)
|
|
1079
|
+
|
|
1080
|
+
def shut_down() -> None:
|
|
1081
|
+
"""Ask for the shutdown SIGTERM asks for: the worker drains, and serve returns."""
|
|
1082
|
+
worker.request_stop()
|
|
1083
|
+
api.should_exit = True
|
|
1084
|
+
|
|
1079
1085
|
worker_task = asyncio.create_task(worker.run())
|
|
1080
1086
|
ready = asyncio.create_task(_announce_ready(api))
|
|
1087
|
+
orphaned = asyncio.create_task(watch_parent(os.getppid, shut_down))
|
|
1081
1088
|
seeding = (
|
|
1082
1089
|
asyncio.create_task(_seed(api, local_url(host, port), bearer, seed, installed)) if seed or installed else None
|
|
1083
1090
|
)
|
|
@@ -1085,6 +1092,7 @@ async def _dev(
|
|
|
1085
1092
|
await api.serve()
|
|
1086
1093
|
finally:
|
|
1087
1094
|
ready.cancel()
|
|
1095
|
+
orphaned.cancel()
|
|
1088
1096
|
if seeding is not None:
|
|
1089
1097
|
seeding.cancel()
|
|
1090
1098
|
worker.request_stop()
|
|
@@ -1107,6 +1115,30 @@ async def _announce_ready(api: "uvicorn.Server") -> None:
|
|
|
1107
1115
|
emit(make("process", at=datetime.now(UTC), message="ready", process="dev"))
|
|
1108
1116
|
|
|
1109
1117
|
|
|
1118
|
+
#: How often the instance looks at whether the process that started it is still there.
|
|
1119
|
+
PARENT_POLL_SECONDS = 1.0
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
async def watch_parent(
|
|
1123
|
+
parent: Callable[[], int], stop: Callable[[], None], *, every: float = PARENT_POLL_SECONDS
|
|
1124
|
+
) -> None:
|
|
1125
|
+
"""Shut the instance down once the process that started it is gone.
|
|
1126
|
+
|
|
1127
|
+
``uv run dg dev`` forwards SIGTERM to the instance, but a wrapper that was killed outright
|
|
1128
|
+
cannot, and the instance it leaves behind goes on claiming runs against the same state.
|
|
1129
|
+
macOS has no PR_SET_PDEATHSIG, so the parent pid is polled: a pid that changed means this
|
|
1130
|
+
process was reparented to init. A dev instance therefore does not outlive the process that
|
|
1131
|
+
started it, backgrounding it past the shell that launched it included.
|
|
1132
|
+
"""
|
|
1133
|
+
import asyncio
|
|
1134
|
+
|
|
1135
|
+
started = parent()
|
|
1136
|
+
while parent() == started:
|
|
1137
|
+
await asyncio.sleep(every)
|
|
1138
|
+
emit(make("process", at=datetime.now(UTC), message="parent gone", process="dev", parent=started))
|
|
1139
|
+
stop()
|
|
1140
|
+
|
|
1141
|
+
|
|
1110
1142
|
async def _seed(
|
|
1111
1143
|
api: "uvicorn.Server",
|
|
1112
1144
|
url: str,
|
|
@@ -16,6 +16,7 @@ from pydantic import BaseModel
|
|
|
16
16
|
from rich.console import Console, Group, RenderableType
|
|
17
17
|
from rich.markup import escape
|
|
18
18
|
from rich.table import Table
|
|
19
|
+
from rich.text import Text
|
|
19
20
|
|
|
20
21
|
from dirigent_client.schemas.common import Problem
|
|
21
22
|
from dirigent_core.protocol import Format, Record, as_json, make
|
|
@@ -500,16 +501,26 @@ def elapsed(value: object) -> str:
|
|
|
500
501
|
return f"{int(hours)}h{int(minutes)}m"
|
|
501
502
|
|
|
502
503
|
|
|
504
|
+
def titled(title: str, built: Table) -> RenderableType:
|
|
505
|
+
"""Put a title above a table, on a line of its own.
|
|
506
|
+
|
|
507
|
+
Rich folds a table's own title to the width of the table, which the widest row sets, so a
|
|
508
|
+
title carrying a 36-character run id breaks in half wherever the rows are narrower than
|
|
509
|
+
it. Above the table the title is folded only by the console.
|
|
510
|
+
"""
|
|
511
|
+
return Group(Text.from_markup(title, style="table.title"), built)
|
|
512
|
+
|
|
513
|
+
|
|
503
514
|
def build_table(title: str, columns: Sequence[str], rows: Iterable[Sequence[str]]) -> RenderableType:
|
|
504
515
|
"""Build one rich table, or the quiet line that stands in for an empty one."""
|
|
505
|
-
built = Table(
|
|
516
|
+
built = Table(show_header=True, header_style="bold")
|
|
506
517
|
for column in columns:
|
|
507
518
|
built.add_column(column)
|
|
508
519
|
count = 0
|
|
509
520
|
for row in rows:
|
|
510
521
|
built.add_row(*row)
|
|
511
522
|
count += 1
|
|
512
|
-
return built if count else f"[dim]{title}: nothing to show.[/]"
|
|
523
|
+
return titled(title, built) if count else f"[dim]{title}: nothing to show.[/]"
|
|
513
524
|
|
|
514
525
|
|
|
515
526
|
def table(title: str, columns: Sequence[str], rows: Iterable[Sequence[str]]) -> None:
|
|
@@ -518,13 +529,13 @@ def table(title: str, columns: Sequence[str], rows: Iterable[Sequence[str]]) ->
|
|
|
518
529
|
|
|
519
530
|
|
|
520
531
|
def build_fields(title: str, values: Mapping[str, Any]) -> RenderableType:
|
|
521
|
-
"""Build one record as a two-column table."""
|
|
522
|
-
built = Table(
|
|
532
|
+
"""Build one record as a two-column table under its title."""
|
|
533
|
+
built = Table(show_header=False, box=None, padding=(0, 2, 0, 0))
|
|
523
534
|
built.add_column(style="bold")
|
|
524
535
|
built.add_column()
|
|
525
536
|
for name, value in values.items():
|
|
526
537
|
built.add_row(name, styled(value))
|
|
527
|
-
return built
|
|
538
|
+
return titled(title, built)
|
|
528
539
|
|
|
529
540
|
|
|
530
541
|
def fields(title: str, values: Mapping[str, Any]) -> None:
|
|
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
|
|
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
|
{dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/templates/pack/__init__.py.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/templates/pack/operator.py.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/templates/pack/pyproject.toml.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.16.3 → dirigent_cli-0.16.5}/src/dirigent_cli/templates/pack/test_plugin.py.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|