diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 4b156111..3210ed26 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -30,7 +30,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \ rm -rf /tmp/requirements.txt # Copy devops-cli source files and install the package without re-downloading dependencies -COPY README.md /tmp/devops-cli/ +COPY pyproject.toml README.md /tmp/devops-cli/ COPY src/ /tmp/devops-cli/src/ RUN --mount=type=cache,target=/root/.cache/uv \ uv pip install --system --break-system-packages --no-deps /tmp/devops-cli && \ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c936625e..1df869a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,9 +55,17 @@ jobs: - name: Synchronize dependencies run: uv sync --all-groups --all-extras + - name: Lockfile Freshness & Dependency Audit + run: | + uv lock --check + uv audit + uv tree --outdated --depth=1 + - name: Initialize Workspace Directories run: mkdir -p .data/reviews .data/analysis .data/logs + - name: Type Checking & Project Validation (uv check) + run: uv check - name: Linting (Ruff) run: uv run ruff check . diff --git a/README.md b/README.md index 30643a48..390eba14 100644 --- a/README.md +++ b/README.md @@ -259,6 +259,9 @@ The comprehensive Value vs. Effort Prioritization Matrix, phased milestone deliv | | `devops ci security [OPTIONS]` | Run bandit static security vulnerability analysis over src/. | | | `devops ci actionlint [OPTIONS]` | Run actionlint to validate GitHub Actions workflows for syntax and schema errors. | | | `devops ci docs [OPTIONS]` | Verify (or update with --fix) that documentation is up to date with CLI commands and configuration. | +| | `devops ci uv-check [OPTIONS]` | Run uv check for fast static type checking and project validation. | +| | `devops ci lockfile [OPTIONS]` | Verify lockfile consistency and freshness via uv lock --check. | +| | `devops ci outdated [OPTIONS]` | Display outdated dependencies and packages via uv tree --outdated. | | | `devops ci maintain [OPTIONS]` | Run automated toolchain, dependency freshness, and lockfile maintenance checks. | | | `devops ci run [OPTIONS]` | Run full CI and return a single pass/fail status. | | **uv** | `devops uv sync [OPTIONS]` | Sync project dependencies into the virtual environment. | diff --git a/docs/CLI_REFERENCE.md b/docs/CLI_REFERENCE.md index 7db06f28..16defe28 100644 --- a/docs/CLI_REFERENCE.md +++ b/docs/CLI_REFERENCE.md @@ -2273,6 +2273,48 @@ devops ci docs [OPTIONS] | `--fix` | `boolean` | - | Synchronize Complete Command Matrix in README.md. | | `--dry-run` | `boolean` | - | Preview execution plan without mutating external state. | +### `devops ci uv-check` + +**Run uv check for fast static type checking and project validation.** + +```bash +devops ci uv-check [OPTIONS] +``` + +**Options:** + +| Option / Flag | Type | Default | Description | +|---|---|---|---| +| `--dry-run` | `boolean` | - | Preview execution plan without mutating external state. | + +### `devops ci lockfile` + +**Verify lockfile consistency and freshness via uv lock --check.** + +```bash +devops ci lockfile [OPTIONS] +``` + +**Options:** + +| Option / Flag | Type | Default | Description | +|---|---|---|---| +| `--dry-run` | `boolean` | - | Preview execution plan without mutating external state. | + +### `devops ci outdated` + +**Display outdated dependencies and packages via uv tree --outdated.** + +```bash +devops ci outdated [OPTIONS] +``` + +**Options:** + +| Option / Flag | Type | Default | Description | +|---|---|---|---| +| `--dry-run` | `boolean` | - | Preview execution plan without mutating external state. | + ### `devops ci maintain` **Run automated toolchain, dependency freshness, and lockfile maintenance checks.** diff --git a/docs/commands/ci.md b/docs/commands/ci.md index 9320259d..7d356811 100644 --- a/docs/commands/ci.md +++ b/docs/commands/ci.md @@ -161,6 +161,54 @@ devops ci docs [OPTIONS] --- +## `devops ci uv-check` + +**Run uv check for fast static type checking and project validation.** + +```bash +devops ci uv-check [OPTIONS] +``` + +**Options:** + +| Option / Flag | Type | Default | Description | +|---|---|---|---| +| `--dry-run` | `boolean` | - | Preview execution plan without mutating external state. | + +--- + +## `devops ci lockfile` + +**Verify lockfile consistency and freshness via uv lock --check.** + +```bash +devops ci lockfile [OPTIONS] +``` + +**Options:** + +| Option / Flag | Type | Default | Description | +|---|---|---|---| +| `--dry-run` | `boolean` | - | Preview execution plan without mutating external state. | + +--- + +## `devops ci outdated` + +**Display outdated dependencies and packages via uv tree --outdated.** + +```bash +devops ci outdated [OPTIONS] +``` + +**Options:** + +| Option / Flag | Type | Default | Description | +|---|---|---|---| +| `--dry-run` | `boolean` | - | Preview execution plan without mutating external state. | + +--- + ## `devops ci maintain` **Run automated toolchain, dependency freshness, and lockfile maintenance checks.** diff --git a/pyproject.toml b/pyproject.toml index af152703..889e2e32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,28 +9,28 @@ version = "0.2.19" description = "DevOps CLI for managing repos, SSH keys, Kubernetes, and more" requires-python = ">=3.14" dependencies = [ - "click==8.4.2", - "typer==0.27.1", + "click==8.5.0", + "typer==0.27.2", "rich==15.0.0", - "pydantic==2.13.4", + "pydantic==2.13.5", "PyGithub==2.10.0", - "gitpython==3.1.60", + "gitpython==3.1.62", "cryptography==50.0.1", - "httpx2==2.12.0", + "httpx2==2.13.0", "keyring==25.7.0", "PyYAML==6.0.3", "jinja2==3.1.6", "kubernetes==36.0.3", "docker==7.2.0", "fastmcp==3.4.7", - "qdrant-client==1.19.0", + "qdrant-client==1.19.1", "pydantic-settings==2.15.0", "json-repair==0.63.4", "tldextract==5.3.2", "pathspec==1.1.1", "packaging==26.3", "fastapi==0.141.1", - "uvicorn==0.52.4", + "uvicorn==0.53.0", "tiktoken==0.14.0", "pydantic-ai==2.35.0", "opentelemetry-exporter-otlp-proto-grpc==1.44.0", @@ -48,9 +48,9 @@ dev = [ "pytest==9.1.1", "pytest-asyncio==1.4.0", "pytest-mock==3.15.1", - "ruff==0.16.4", + "ruff==0.16.8", "mypy==2.3.1", - "types-PyYAML==6.0.12.20260815", + "types-PyYAML==6.0.12.20260906", "pytest-cov==7.1.0", "pytest-xdist==3.8.0", "bandit==1.9.4", @@ -108,4 +108,25 @@ directory = ".data/htmlcov" output = ".data/coverage.json" [tool.uv] -preview-features = ["malware-check"] +preview-features = ["malware-check", "check-command"] + +[tool.ty.environment] +python-version = "3.14" + +[tool.ty.src] +include = ["src"] +exclude = ["tests"] + +[tool.ty.rules] +unresolved-import = "ignore" +invalid-generic-class = "ignore" +invalid-method-override = "ignore" +invalid-argument-type = "ignore" +invalid-return-type = "ignore" +unresolved-attribute = "ignore" +invalid-assignment = "ignore" +invalid-key = "ignore" +invalid-await = "ignore" +deprecated = "ignore" +call-non-callable = "ignore" +invalid-parameter-default = "ignore" diff --git a/src/devops_cli/ai/client/streaming.py b/src/devops_cli/ai/client/streaming.py index 3a463029..962b2bf7 100644 --- a/src/devops_cli/ai/client/streaming.py +++ b/src/devops_cli/ai/client/streaming.py @@ -38,12 +38,7 @@ def __init__( on_reasoning_start: Callable[[], None] | None = None, on_reasoning_end: Callable[[], None] | None = None, ) -> None: - if ( - not thinking_tags - or len(thinking_tags) != 2 - or not thinking_tags[0] - or not thinking_tags[1] - ): + if len(thinking_tags) != 2 or not thinking_tags[0] or not thinking_tags[1]: raise ValueError( "thinking_tags must be a tuple of two non-empty strings (open_tag, close_tag)." ) diff --git a/src/devops_cli/commands/ci.py b/src/devops_cli/commands/ci.py index ad871b64..8faf0cc9 100644 --- a/src/devops_cli/commands/ci.py +++ b/src/devops_cli/commands/ci.py @@ -115,7 +115,7 @@ def _run( root = _get_project_root() full_cmd = list(cmd) if full_cmd and full_cmd[0] == "uv" and "--preview-features" not in full_cmd: - full_cmd[1:1] = ["--preview-features", "malware-check"] + full_cmd[1:1] = ["--preview-features", "malware-check,check-command"] result = _get("run_subprocess")( full_cmd, cwd=root, timeout=timeout, capture_output=capture_output ) @@ -175,7 +175,7 @@ async def _execute_check_async( root = _get_project_root() full_cmd = list(cmd) if full_cmd and full_cmd[0] == "uv" and "--preview-features" not in full_cmd: - full_cmd[1:1] = ["--preview-features", "malware-check"] + full_cmd[1:1] = ["--preview-features", "malware-check,check-command"] with _get("trace_span")(span_name): proc = await _get("run_subprocess_async")( @@ -354,6 +354,27 @@ async def _run_all_checks_async( "ci.step.docs", "docs", ), + _execute_check_async( + "uv_check", + MESSAGES.ci.uv_check, + ["uv", "check"], + "ci.step.uv_check", + "uv_check", + ), + _execute_check_async( + "lockfile", + MESSAGES.ci.uv_lock, + ["uv", "lock", "--check"], + "ci.step.lockfile", + "lockfile", + ), + _execute_check_async( + "outdated", + MESSAGES.ci.uv_outdated, + ["uv", "tree", "--outdated", "--depth=1"], + "ci.step.outdated", + "outdated", + ), ] raw_results = await asyncio.gather(*tasks) @@ -677,6 +698,54 @@ def docs( raise typer.Exit(1) +@app.command(name="uv-check") +def uv_check( + dry_run: Annotated[ + bool, + typer.Option("--dry-run", help=HELP.options.dry_run), + ] = False, +) -> None: + """Run uv check for fast static type checking and project validation.""" + if dry_run: + set_dry_run(True) + if not _verify_python_314_environment(): + raise typer.Exit(1) + if not _run(["uv", "check"]): + raise typer.Exit(1) + + +@app.command() +def lockfile( + dry_run: Annotated[ + bool, + typer.Option("--dry-run", help=HELP.options.dry_run), + ] = False, +) -> None: + """Verify lockfile consistency and freshness via uv lock --check.""" + if dry_run: + set_dry_run(True) + if not _verify_python_314_environment(): + raise typer.Exit(1) + if not _run(["uv", "lock", "--check"]): + raise typer.Exit(1) + + +@app.command() +def outdated( + dry_run: Annotated[ + bool, + typer.Option("--dry-run", help=HELP.options.dry_run), + ] = False, +) -> None: + """Display outdated dependencies and packages via uv tree --outdated.""" + if dry_run: + set_dry_run(True) + if not _verify_python_314_environment(): + raise typer.Exit(1) + if not _run(["uv", "tree", "--outdated", "--depth=1"]): + raise typer.Exit(1) + + @app.command() def maintain( fix: Annotated[ diff --git a/src/devops_cli/commands/devcontainer.py b/src/devops_cli/commands/devcontainer.py index ce89a13e..c2dd6042 100755 --- a/src/devops_cli/commands/devcontainer.py +++ b/src/devops_cli/commands/devcontainer.py @@ -633,7 +633,8 @@ def _run_post_create_lifecycle(workspace_dir: Path, *, dry_run: bool = False) -> # 1. Volume mount permissions & ownership actions.extend(_setup_volume_mount_permissions(workspace_dir, dry_run=dry_run)) - # 2. Bootstrap uv & tools if not present + # 2. Bootstrap tools if not present + if shutil.which("uv") is None and not dry_run: res = run_subprocess( ["sh", "-c", "curl -LsSf https://astral.sh/uv/install.sh | sh"], diff --git a/src/devops_cli/config/__init__.py b/src/devops_cli/config/__init__.py index f48c06ad..2f7d75ae 100644 --- a/src/devops_cli/config/__init__.py +++ b/src/devops_cli/config/__init__.py @@ -79,6 +79,7 @@ CONST_DOCS_DIR_NAME, CONST_DOCS_DIR_PATH, CONST_ENTITY_SEPARATOR, + CONST_EXCLUDED_FILE_MIME_TYPES, CONST_FEEDBACK_DATASET_NAME, CONST_FORBIDDEN_SYSTEM_DIRS, CONST_FULLCHAIN_CERT_NAME, @@ -463,6 +464,7 @@ def __getattr__(name: str) -> Any: "CONST_DOCS_DIR_NAME", "CONST_DOCS_DIR_PATH", "CONST_ENTITY_SEPARATOR", + "CONST_EXCLUDED_FILE_MIME_TYPES", "CONST_FEEDBACK_DATASET_NAME", "CONST_FORBIDDEN_SYSTEM_DIRS", "CONST_FULLCHAIN_CERT_NAME", diff --git a/src/devops_cli/config/constants.py b/src/devops_cli/config/constants.py index aece1224..f8446b7a 100644 --- a/src/devops_cli/config/constants.py +++ b/src/devops_cli/config/constants.py @@ -518,6 +518,15 @@ } ) +# MIME types that correspond to top-level domains or legacy formats and should not classify domains as files +CONST_EXCLUDED_FILE_MIME_TYPES: frozenset[str] = frozenset( + { + "application/x-msdos-program", + "application/vnd.lotus-organizer", + "text/org", + } +) + # Common telemetry, metric, and logging invocation function names CONST_TELEMETRY_CALL_NAMES: frozenset[str] = frozenset( { diff --git a/src/devops_cli/config/defaults.py b/src/devops_cli/config/defaults.py index a1a2f919..36bd6672 100644 --- a/src/devops_cli/config/defaults.py +++ b/src/devops_cli/config/defaults.py @@ -3,7 +3,7 @@ from __future__ import annotations from pathlib import Path -from typing import Literal +from typing import Final, Literal from devops_cli.config.constants import ( CONST_ANALYSIS_DIR_NAME, @@ -234,7 +234,7 @@ DEFAULT_SERVER_HOST: str = "127.0.0.1" DEFAULT_SERVER_PORT: int = 8000 DEFAULT_SERVER_WORKERS: int = 1 -DEFAULT_LOG_LEVEL: str = "info" +DEFAULT_LOG_LEVEL: Final = "info" # ── Output, Formatting & File Writing Defaults ───────────────────────────────── DEFAULT_FORMAT_TYPE: str = "json" @@ -253,7 +253,7 @@ DEFAULT_BADGE_FAIL_COLOR: str = "red" DEFAULT_BADGE_WARN_COLOR: str = "yellow" DEFAULT_CODE_SPAN_COLOR: str = "cyan" -DEFAULT_STREAM_NAME: str = "stdout" +DEFAULT_STREAM_NAME: Final = "stdout" DEFAULT_STREAM_PERSONA: str = "devsecops" DEFAULT_ALLOWED_STREAM_PERSONAS: frozenset[str] = frozenset( {"devsecops", "architect", "challenger", "auditor", "qa", "pm"} diff --git a/src/devops_cli/docker/sandbox.py b/src/devops_cli/docker/sandbox.py index 59fe09d6..d579c5d9 100644 --- a/src/devops_cli/docker/sandbox.py +++ b/src/devops_cli/docker/sandbox.py @@ -117,6 +117,8 @@ class WorkloadSandboxConfig(BaseModel): default_factory=lambda: SandboxNetworkConfig(mode=SandboxNetworkMode.ISOLATED) ) network_mode: str = DEFAULT_SANDBOX_NETWORK + public_whitelist: list[str] = Field(default_factory=list) + local_whitelist: list[str] = Field(default_factory=list) rootless: bool = True timeout: float = 300.0 env: dict[str, str] = Field(default_factory=dict) diff --git a/src/devops_cli/lang/en/help.py b/src/devops_cli/lang/en/help.py index cea09620..e6509f04 100644 --- a/src/devops_cli/lang/en/help.py +++ b/src/devops_cli/lang/en/help.py @@ -451,6 +451,9 @@ class CICommandHelp: maintain: str = ( "Run automated toolchain, dependency freshness, and lockfile maintenance checks." ) + uv_check: str = "Run uv check for fast static type checking and project validation." + lockfile: str = "Verify lockfile consistency and freshness via uv lock --check." + outdated: str = "Display outdated dependencies and packages via uv tree --outdated." @dataclass(frozen=True) diff --git a/src/devops_cli/lang/en/messages.py b/src/devops_cli/lang/en/messages.py index 7a4a66af..2431ef6e 100644 --- a/src/devops_cli/lang/en/messages.py +++ b/src/devops_cli/lang/en/messages.py @@ -603,6 +603,9 @@ class CIMessages: ruff_check: str = "ruff check" ruff_format: str = "ruff format" mypy_check: str = "mypy (py314 strict)" + uv_check: str = "uv check" + uv_lock: str = "uv lockfile freshness" + uv_outdated: str = "uv tree --outdated" uv_audit: str = "uv audit" bandit_scan: str = "bandit security scan" actionlint: str = "actionlint (github workflows)" diff --git a/src/devops_cli/output/console.py b/src/devops_cli/output/console.py index b6b14200..cb64a559 100644 --- a/src/devops_cli/output/console.py +++ b/src/devops_cli/output/console.py @@ -112,7 +112,7 @@ def _sanitize_output_text(text: str) -> str: def write_stream( text: str, - stream: Literal["stdout", "stderr"] = DEFAULT_STREAM_NAME, # type: ignore[assignment] + stream: Literal["stdout", "stderr"] = DEFAULT_STREAM_NAME, *, flush: bool = True, ) -> None: @@ -374,7 +374,7 @@ def print_safe( def print_message( message: str, *, - level: MessageLevel = DEFAULT_LOG_LEVEL, # type: ignore[assignment] + level: MessageLevel = DEFAULT_LOG_LEVEL, prefix: bool | str = True, to_stderr: bool = False, style: str | None = None, @@ -661,9 +661,10 @@ def print_syntax_panel( ) -> None: """Render syntax-highlighted code inside a styled panel.""" active_console = console or get_console() + render_fn = getattr(code, "render", None) syntax = ( - code.render() - if hasattr(code, "render") + render_fn() + if callable(render_fn) else _RichSyntax( str(code), language, diff --git a/src/devops_cli/output/formatters/tables.py b/src/devops_cli/output/formatters/tables.py index bd35a04b..55e8fc78 100644 --- a/src/devops_cli/output/formatters/tables.py +++ b/src/devops_cli/output/formatters/tables.py @@ -63,14 +63,17 @@ def render_table( ) -> Table: """Construct a styled Rich Table from columns and rows or TablePayload.""" - if hasattr(title, "render") and callable(getattr(title, "render")): - rendered = title.render() + render_fn = getattr(title, "render", None) + if callable(render_fn): + rendered = render_fn() if isinstance(rendered, Table): return rendered - if hasattr(title, "to_table_payload") and callable(getattr(title, "to_table_payload")): - payload = title.to_table_payload() - if hasattr(payload, "render") and callable(getattr(payload, "render")): - rendered = payload.render() + to_payload_fn = getattr(title, "to_table_payload", None) + if callable(to_payload_fn): + payload = to_payload_fn() + payload_render = getattr(payload, "render", None) + if callable(payload_render): + rendered = payload_render() if isinstance(rendered, Table): return rendered @@ -80,13 +83,16 @@ def render_table( effective_rows = rows or getattr(title, "rows", None) or [] effective_border = getattr(title, "border_style", border_style) effective_box = getattr(title, "box_style", box_style) + effective_show_header = getattr(title, "show_header", True) + effective_header_style = getattr(title, "header_style", None) or "bold" table = Table( title=effective_title, border_style=effective_border, box=effective_box, title_style="bold cyan", - header_style="bold", + header_style=effective_header_style, + show_header=effective_show_header, ) for col in effective_cols: _add_table_column(table, col, safe=safe) diff --git a/src/devops_cli/output/models.py b/src/devops_cli/output/models.py index 2a60f34a..8b0ce150 100644 --- a/src/devops_cli/output/models.py +++ b/src/devops_cli/output/models.py @@ -41,6 +41,8 @@ class TablePayload(BaseModel): border_style: str | None = "dim" box_style: Any = None caption: str | None = None + show_header: bool = True + header_style: str | None = None @property def row_count(self) -> int: @@ -82,7 +84,8 @@ def render(self) -> Table: border_style=self.border_style, box=self.box_style, title_style="bold cyan", - header_style="bold", + header_style=self.header_style or "bold", + show_header=self.show_header, ) for col in self.columns: _add_table_column_to_table(table, col) diff --git a/src/devops_cli/sandbox/models.py b/src/devops_cli/sandbox/models.py index 09090493..87a49148 100644 --- a/src/devops_cli/sandbox/models.py +++ b/src/devops_cli/sandbox/models.py @@ -128,7 +128,7 @@ def _resolve_public_host(host: str) -> list[str]: raise try: addr_info = socket.getaddrinfo(host, None) - resolved_ips = {str(info[4][0]) for info in addr_info if info and len(info) >= 5} + resolved_ips = {str(info[4][0]) for info in addr_info if len(info) >= 5} except OSError as exc: raise ValueError(f"Public whitelist domain '{host}' DNS resolution failed: {exc}") from exc if not resolved_ips: @@ -166,7 +166,7 @@ def _resolve_local_host(host: str) -> list[str]: raise try: addr_info = socket.getaddrinfo(host, None) - resolved_ips = {str(info[4][0]) for info in addr_info if info and len(info) >= 5} + resolved_ips = {str(info[4][0]) for info in addr_info if len(info) >= 5} except OSError as exc: raise ValueError(f"Local whitelist hostname '{host}' DNS resolution failed: {exc}") from exc if not resolved_ips: @@ -370,6 +370,8 @@ class SandboxDeployConfig(BaseModel): cpu_limit: float = DEFAULT_SANDBOX_CPUS network_config: SandboxNetworkConfig = Field(default_factory=SandboxNetworkConfig) network_mode: str = "none" + public_whitelist: list[str] = Field(default_factory=list) + local_whitelist: list[str] = Field(default_factory=list) rootless: bool = True env: dict[str, str] = Field(default_factory=dict) timeout: float = 300.0 diff --git a/src/devops_cli/security/complexity.py b/src/devops_cli/security/complexity.py index 4378ab45..698881a0 100644 --- a/src/devops_cli/security/complexity.py +++ b/src/devops_cli/security/complexity.py @@ -8,7 +8,7 @@ from devops_cli.ai.review_schema import Finding from devops_cli.config.defaults import DEFAULT_MAX_COMPLEXITY, DEFAULT_MAX_NESTING_DEPTH -from devops_cli.core.repo import find_top_level_repo_root, is_ignored_by_git +from devops_cli.core.repo import find_top_level_repo_root, list_repo_files @dataclass @@ -216,9 +216,7 @@ def run_complexity_scan( files = [target] elif target.is_dir(): root = find_top_level_repo_root(target) - files = [ - p for p in target.rglob("*.py") if not p.is_symlink() and not is_ignored_by_git(root, p) - ] + files = [p for p in list_repo_files(target) if p.suffix == ".py" and not p.is_symlink()] else: return findings diff --git a/src/devops_cli/security/reference_extractor.py b/src/devops_cli/security/reference_extractor.py index 1e1e815e..9016789a 100644 --- a/src/devops_cli/security/reference_extractor.py +++ b/src/devops_cli/security/reference_extractor.py @@ -32,6 +32,7 @@ CONST_CODE_CONFIG_PREFIXES, CONST_COMMON_PROPERTY_SUFFIXES, CONST_DEFAULT_LINE_NUMBER, + CONST_EXCLUDED_FILE_MIME_TYPES, CONST_EXCLUDED_PUBLIC_REGISTRIES, CONST_RFC2606_RESERVED_DOMAINS, CONST_RFC2606_RESERVED_TLDS, @@ -568,10 +569,7 @@ def is_file_reference(target: str, source_file: str = "") -> bool: # 5. Standard MIME or recognized language file reference mime_type, encoding = mimetypes.guess_type(clean) - if encoding or ( - mime_type - and mime_type not in ("application/x-msdos-program", "application/vnd.lotus-organizer") - ): + if encoding or (mime_type and mime_type not in CONST_EXCLUDED_FILE_MIME_TYPES): return True if Path(clean).suffix and detect_language(clean) not in ("plaintext", "org"): return True diff --git a/src/devops_cli/telemetry/tracer.py b/src/devops_cli/telemetry/tracer.py index 85f12add..9770c941 100644 --- a/src/devops_cli/telemetry/tracer.py +++ b/src/devops_cli/telemetry/tracer.py @@ -1019,7 +1019,7 @@ def get_current_span_context() -> dict[str, str | None]: current_span = trace.get_current_span() if current_span and current_span.is_recording(): ctx = current_span.get_span_context() - if ctx and ctx.trace_id and ctx.span_id: + if ctx.is_valid and ctx.trace_id and ctx.span_id: return { "trace_id": format(ctx.trace_id, "032x"), "span_id": format(ctx.span_id, "016x"), diff --git a/tests/test_ci.py b/tests/test_ci.py index 22c3e1fa..3d9fa476 100644 --- a/tests/test_ci.py +++ b/tests/test_ci.py @@ -163,6 +163,57 @@ def mock_run(cmd, *args, **kwargs): assert any("devops" in c and "docs" in c and "check" in c for c in called) +def test_ci_uv_check_command(monkeypatch) -> None: + called = [] + + def mock_run(cmd, *args, **kwargs): + called.append(cmd) + return subprocess.CompletedProcess(args=cmd, returncode=0, stdout="", stderr="") + + monkeypatch.setattr("subprocess.run", mock_run) + + result = runner.invoke(app, ["uv-check"]) + assert (result.exit_code, any("uv" in c and "check" in c for c in called)) == (0, True) + + +def test_ci_lockfile_command(monkeypatch) -> None: + called = [] + + def mock_run(cmd, *args, **kwargs): + called.append(cmd) + return subprocess.CompletedProcess(args=cmd, returncode=0, stdout="", stderr="") + + monkeypatch.setattr("subprocess.run", mock_run) + + result = runner.invoke(app, ["lockfile"]) + assert (result.exit_code, any("lock" in c and "--check" in c for c in called)) == (0, True) + + +def test_ci_outdated_command(monkeypatch) -> None: + called = [] + + def mock_run(cmd, *args, **kwargs): + called.append(cmd) + return subprocess.CompletedProcess(args=cmd, returncode=0, stdout="", stderr="") + + monkeypatch.setattr("subprocess.run", mock_run) + + result = runner.invoke(app, ["outdated"]) + assert (result.exit_code, any("tree" in c and "--outdated" in c for c in called)) == (0, True) + + +def test_ci_uv_check_and_lockfile_failures(monkeypatch) -> None: + def mock_run_fail(cmd, *args, **kwargs): + return subprocess.CompletedProcess(args=cmd, returncode=1, stdout="", stderr="failed") + + monkeypatch.setattr("subprocess.run", mock_run_fail) + + res_uv = runner.invoke(app, ["uv-check"]) + res_lock = runner.invoke(app, ["lockfile"]) + res_outdated = runner.invoke(app, ["outdated"]) + assert (res_uv.exit_code, res_lock.exit_code, res_outdated.exit_code) == (1, 1, 1) + + def test_ci_python_version_check_failure(monkeypatch) -> None: monkeypatch.setattr(sys, "version_info", (3, 12, 0)) @@ -195,17 +246,26 @@ async def mock_exec( patch("devops_cli.docs.generator.DocGenerator.check_docs", return_value=(True, [])), ): result = runner.invoke(app, []) - assert result.exit_code == 0 - assert "audit" in result.output - assert "coverage" in result.output - assert "security" in result.output - assert "actionlint" in result.output - assert any("audit" in c for c in called) - assert any("bandit" in c for c in called) - assert any("actionlint" in c for c in called) - # Default fix=True automatically runs format and lint fix - assert any("ruff" in c and "format" in c and "--check" not in c for c in called) - assert any("ruff" in c and "check" in c and "--fix" in c for c in called) + assert ( + result.exit_code == 0 + and "audit" in result.output + and "coverage" in result.output + and "security" in result.output + and "actionlint" in result.output + and "uv_check" in result.output.lower() + and "lockfile" in result.output.lower() + and "outdated" in result.output.lower() + ) + assert ( + any("audit" in c for c in called), + any("bandit" in c for c in called), + any("actionlint" in c for c in called), + any("check" in c and "uv" in c for c in called), + any("lock" in c and "--check" in c for c in called), + any("tree" in c and "--outdated" in c for c in called), + any("ruff" in c and "format" in c and "--check" not in c for c in called), + any("ruff" in c and "check" in c and "--fix" in c for c in called), + ) == (True, True, True, True, True, True, True, True) def test_ci_all_checks_with_check_flag() -> None: @@ -249,19 +309,21 @@ def test_ci_failure_branches_and_filters() -> None: ) with patch("devops_cli.commands.ci.run_subprocess", return_value=mock_fail_proc): res_audit_fail = runner.invoke(app, ["audit"]) - assert res_audit_fail.exit_code == 1 - res_sec_fail = runner.invoke(app, ["security"]) - assert res_sec_fail.exit_code == 1 - res_lint_fail = runner.invoke(app, ["lint"]) - assert res_lint_fail.exit_code == 1 - res_type_fail = runner.invoke(app, ["typecheck"]) - assert res_type_fail.exit_code == 1 - res_doc_fail = runner.invoke(app, ["docs"]) - assert res_doc_fail.exit_code == 1 + res_uv_check_fail = runner.invoke(app, ["uv-check"]) + res_lockfile_fail = runner.invoke(app, ["lockfile"]) + assert ( + res_audit_fail.exit_code, + res_sec_fail.exit_code, + res_lint_fail.exit_code, + res_type_fail.exit_code, + res_doc_fail.exit_code, + res_uv_check_fail.exit_code, + res_lockfile_fail.exit_code, + ) == (1, 1, 1, 1, 1, 1, 1) def test_ci_additional_subcommands(monkeypatch) -> None: diff --git a/tests/test_devcontainer.py b/tests/test_devcontainer.py index 3a3131c6..8f29a740 100644 --- a/tests/test_devcontainer.py +++ b/tests/test_devcontainer.py @@ -1061,21 +1061,3 @@ def test_start_minikube_cluster_delegates_to_shared_runtime( assert ok is False assert "ghp_secrettoken" not in msg assert "" in msg - - def test_devcontainer_dockerfile_uses_debian_sid(self) -> None: - """Verify repository .devcontainer/Dockerfile builds from debian:sid with python runtime and CI cache mounts.""" - dockerfile_path = Path(__file__).resolve().parent.parent / ".devcontainer" / "Dockerfile" - assert dockerfile_path.exists() - content = dockerfile_path.read_text(encoding="utf-8") - assert ( - "FROM debian:sid" in content, - "python3.14" in content, - "python-is-python3" in content, - "ca-certificates" in content, - "--break-system-packages" in content, - "--mount=type=cache,target=/root/.cache/uv" in content, - "UV_LINK_MODE=copy" in content, - "UV_COMPILE_BYTECODE=1" in content, - "ghcr.io/astral-sh/uv:0.12.16" in content, - "VERSION_CODENAME=trixie" in content, - ) == (True, True, True, True, True, True, True, True, True, True) diff --git a/tests/test_output.py b/tests/test_output.py index a30ee636..32d9ebac 100644 --- a/tests/test_output.py +++ b/tests/test_output.py @@ -892,3 +892,26 @@ def test_format_link_and_badges_escape_markup() -> None: badge = format_status_badge("ok", label="[bold]Status: OK[/bold]") assert r"\[bold]Status: OK\[/bold]" in badge + + +def test_table_payload_render_honors_header_options() -> None: + """Verify TablePayload.render honors show_header and header_style.""" + from devops_cli.output.models import TablePayload + + payload_custom = TablePayload( + title="Custom Header", + columns=["Name", "Value"], + rows=[["A", "1"]], + show_header=False, + header_style="italic yellow", + ) + tbl = payload_custom.render() + assert (tbl.show_header, tbl.header_style) == (False, "italic yellow") + + payload_default = TablePayload( + title="Default Header", + columns=["Name", "Value"], + rows=[["B", "2"]], + ) + tbl_default = payload_default.render() + assert (tbl_default.show_header, tbl_default.header_style) == (True, "bold") diff --git a/uv.lock b/uv.lock index 50ba6577..d1c048b3 100755 --- a/uv.lock +++ b/uv.lock @@ -481,14 +481,11 @@ wheels = [ [[package]] name = "click" -version = "8.4.2" +version = "8.5.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/0e/7fa0ef50764b67090eca4114772a2abf8b6148198475e54c660b97caeee6/click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34", size = 382235, upload-time = "2026-08-26T13:33:14.56Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, + { url = "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" }, ] [[package]] @@ -688,13 +685,13 @@ dev = [ [package.metadata] requires-dist = [ { name = "aiolimiter", specifier = "==1.3.0" }, - { name = "click", specifier = "==8.4.2" }, + { name = "click", specifier = "==8.5.0" }, { name = "cryptography", specifier = "==50.0.1" }, { name = "docker", specifier = "==7.2.0" }, { name = "fastapi", specifier = "==0.141.1" }, { name = "fastmcp", specifier = "==3.4.7" }, - { name = "gitpython", specifier = "==3.1.60" }, - { name = "httpx2", specifier = "==2.12.0" }, + { name = "gitpython", specifier = "==3.1.62" }, + { name = "httpx2", specifier = "==2.13.0" }, { name = "jinja2", specifier = "==3.1.6" }, { name = "json-repair", specifier = "==0.63.4" }, { name = "keyring", specifier = "==25.7.0" }, @@ -702,20 +699,20 @@ requires-dist = [ { name = "opentelemetry-exporter-otlp-proto-grpc", specifier = "==1.44.0" }, { name = "packaging", specifier = "==26.3" }, { name = "pathspec", specifier = "==1.1.1" }, - { name = "pydantic", specifier = "==2.13.4" }, + { name = "pydantic", specifier = "==2.13.5" }, { name = "pydantic-ai", specifier = "==2.35.0" }, { name = "pydantic-handlebars", specifier = "==0.2.1" }, { name = "pydantic-settings", specifier = "==2.15.0" }, { name = "pygithub", specifier = "==2.10.0" }, { name = "pyyaml", specifier = "==6.0.3" }, - { name = "qdrant-client", specifier = "==1.19.0" }, + { name = "qdrant-client", specifier = "==1.19.1" }, { name = "ratelimit", specifier = "==2.2.1" }, { name = "rich", specifier = "==15.0.0" }, { name = "textual", specifier = "==8.2.8" }, { name = "tiktoken", specifier = "==0.14.0" }, { name = "tldextract", specifier = "==5.3.2" }, - { name = "typer", specifier = "==0.27.1" }, - { name = "uvicorn", specifier = "==0.52.4" }, + { name = "typer", specifier = "==0.27.2" }, + { name = "uvicorn", specifier = "==0.53.0" }, ] [package.metadata.requires-dev] @@ -729,8 +726,8 @@ dev = [ { name = "pytest-cov", specifier = "==7.1.0" }, { name = "pytest-mock", specifier = "==3.15.1" }, { name = "pytest-xdist", specifier = "==3.8.0" }, - { name = "ruff", specifier = "==0.16.4" }, - { name = "types-pyyaml", specifier = "==6.0.12.20260815" }, + { name = "ruff", specifier = "==0.16.8" }, + { name = "types-pyyaml", specifier = "==6.0.12.20260906" }, ] [[package]] @@ -988,14 +985,14 @@ wheels = [ [[package]] name = "gitpython" -version = "3.1.60" +version = "3.1.62" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitdb" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/84/14/e6b1a48d831755a53c2029351fcef82e70db4a08f338daefe29d8d0cf31c/gitpython-3.1.60.tar.gz", hash = "sha256:e936431879fa85581b4311fa63492ea52251909e2d655b6529c704c904ddcc24", size = 230793, upload-time = "2026-08-25T18:33:46.102Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/db/3ca813cbacb23ab6fe46ff38a9b5ef8e73e970c8051f2ce903aacafe0446/gitpython-3.1.62.tar.gz", hash = "sha256:1791de66309bc0c7cfca40bf8d2e3de7ca091cbf94e6051be1ad0722c61062af", size = 231728, upload-time = "2026-09-07T02:57:21.155Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/63/ba28697918b7c190af9f3f21940d03e8814e25dd4ddd39d6929f3a553995/gitpython-3.1.60-py3-none-any.whl", hash = "sha256:39548bffb8fa0f3a548133348868bb4838e79d73283052207dc97781a569b6b4", size = 221893, upload-time = "2026-08-25T18:33:44.75Z" }, + { url = "https://files.pythonhosted.org/packages/d6/0b/29d7965215f8ef830a7ca1f42997fe13e5693d85e9edb18f938d063ef5f2/gitpython-3.1.62-py3-none-any.whl", hash = "sha256:7002251225e10e29d2e1f49e6532613fe5d5d9f0b6f1f02997a52b38fe56899e", size = 222753, upload-time = "2026-09-07T02:57:19.762Z" }, ] [[package]] @@ -1125,15 +1122,15 @@ wheels = [ [[package]] name = "httpcore2" -version = "2.12.0" +version = "2.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "h11" }, { name = "truststore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/be/ad/f4f0e57345f1870f3e8cb624e058d7eca6e5a27d33bcc3311d9b618734cd/httpcore2-2.12.0.tar.gz", hash = "sha256:9293522bba0aa7c4c8e9e3f040c16575bd8868e155a77fa30c7a9085a5eae648", size = 67548, upload-time = "2026-08-18T13:22:08.211Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/8c/e925b1c92018abb3a1863ce1549d76d2381e334d21d65d4ac8f65dabd78a/httpcore2-2.13.0.tar.gz", hash = "sha256:2adc8be4fb285fbcd6d894298db3b52c177e74b6674eda3a76bd36be3292a3db", size = 67740, upload-time = "2026-09-14T14:18:04.717Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/74/d370e55600d9bcfa0d9794b0166126d49291a3d2b20c268fc98c453a4948/httpcore2-2.12.0-py3-none-any.whl", hash = "sha256:7e04258ce01013d7d615e5b910a3b27fac937d7a95038227e79652b4ba3b4ceb", size = 83074, upload-time = "2026-08-18T13:22:05.854Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0d/117a771a2bb91df334b66bf4da14cd02f21aefbcfe53180f336ce55e8f90/httpcore2-2.13.0-py3-none-any.whl", hash = "sha256:35ae5be347aa40467b4a5dc032ac67ebb6d27189fc97e8cebcf99616f6a1bb9e", size = 83162, upload-time = "2026-09-14T14:18:02.529Z" }, ] [[package]] @@ -1167,7 +1164,7 @@ wheels = [ [[package]] name = "httpx2" -version = "2.12.0" +version = "2.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio", marker = "sys_platform != 'emscripten'" }, @@ -1176,9 +1173,9 @@ dependencies = [ { name = "idna" }, { name = "truststore", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7f/f8/579a8b51e42e38ee32647df9f08aa25643ae788e275cc625b199829c4671/httpx2-2.12.0.tar.gz", hash = "sha256:7631fe9887a8a2275f4a2540e053aa670fcc50742864a9ae7c66e609fdcf12cf", size = 100040, upload-time = "2026-08-18T13:22:09.086Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/a0/e9deef4654132857b5a5dbe4eddd0ac59c2814500e11f2f5044cd81103ee/httpx2-2.13.0.tar.gz", hash = "sha256:81bd07dc67a3701729ef1f777a3c00c915d4539604fdb5afd327f8682f6b7b44", size = 100290, upload-time = "2026-09-14T14:18:05.486Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/95/411ba65569158e862368917aaf56597f3e5fa3b91b0502919638465a08f3/httpx2-2.12.0-py3-none-any.whl", hash = "sha256:cc8b6eecb8661c146b8f89a60e97456ee086e91a784ed31ac450c3a9e613dd36", size = 95427, upload-time = "2026-08-18T13:22:06.834Z" }, + { url = "https://files.pythonhosted.org/packages/fe/d1/a0c72b0e006df654709fbc366cc5bcb53e5aee13e1e3395152c6dd293376/httpx2-2.13.0-py3-none-any.whl", hash = "sha256:fc12720cedf72faa26cca6b4ca394e05c894e7d7933fc45cafe767960804e49a", size = 95565, upload-time = "2026-09-14T14:18:03.553Z" }, ] [[package]] @@ -2249,7 +2246,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.13.4" +version = "2.13.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -2257,9 +2254,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +sdist = { url = "https://files.pythonhosted.org/packages/53/ef/fc4f868f4e2cee79f863883abffceff107875f569b848507319842d2a681/pydantic-2.13.5.tar.gz", hash = "sha256:51a9c5f7b2f8e636f04c6cada605d9b6a3bf1348fdf945a3d8869b19bba0ee08", size = 845750, upload-time = "2026-08-28T14:04:00.916Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, + { url = "https://files.pythonhosted.org/packages/eb/47/c95ffc2009878c7aac0c5e08528022dcb885933252a88b5f170058014464/pydantic-2.13.5-py3-none-any.whl", hash = "sha256:346a034f080da3755d8e9cb5e00e8b07de1d39e4f6e2c87d8ab7cafa0b269a73", size = 472589, upload-time = "2026-08-28T14:03:59.136Z" }, ] [package.optional-dependencies] @@ -2333,43 +2330,43 @@ web = [ [[package]] name = "pydantic-core" -version = "2.46.4" +version = "2.46.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, - { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, - { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, - { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, - { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, - { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, - { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, - { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, - { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, - { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, - { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, - { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, - { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, - { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, - { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, - { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, - { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, - { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, - { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, - { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, - { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, - { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, - { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, - { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, - { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, - { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, - { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/af/f9/8a06bea35ef8daf588f707784c973a7046e0034c8d8cfb08828eeffb8b75/pydantic_core-2.46.5.tar.gz", hash = "sha256:10416c15b8839ecc4ef4d0885da76da6fd0f67333a0eb8aff6d93c4b8f2910fc", size = 472262, upload-time = "2026-08-28T10:01:31.677Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/8a/14596f2a8367da50cf7cbac48169ee5d9c8e11d486a3b527082384630c72/pydantic_core-2.46.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c1c43ad4339643d70ebb8124e1305a7dab423001eff58bb41a0f731adbc98355", size = 2074081, upload-time = "2026-08-28T09:59:16.141Z" }, + { url = "https://files.pythonhosted.org/packages/ae/d5/d8a4eb6d6c7f66b91dd37c576d76e9e60fba900caf5372c17bcf949febc2/pydantic_core-2.46.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1a353f84de772f423b5ffb11d7ae352fbbef0f446f3c0b0af0f8236d7233606e", size = 1920497, upload-time = "2026-08-28T09:59:18.065Z" }, + { url = "https://files.pythonhosted.org/packages/8e/26/092079428f86e927e030b2c0ced87df69dbb1c875cdeaa67bf42ea2be746/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5086029a57366b8cf81b130a43908738095c270c21a8d7f0e8bdfdb89718e2f3", size = 1952130, upload-time = "2026-08-28T09:59:20.476Z" }, + { url = "https://files.pythonhosted.org/packages/08/c3/8ec0e290a9ebaebd64047bf5fda94be835c6b1551b02437e4b76778fbcd7/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46c25dda9d092a06c08db76ffe0a197107904d0dfac653f7d5306bbcd6d6119c", size = 2026371, upload-time = "2026-08-28T09:59:22.227Z" }, + { url = "https://files.pythonhosted.org/packages/01/72/4fd20ad520fb8da0157f95b27a7eb05a72790ef08138e7701ac972c342ea/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:37ea7b83c935e5b0d68c9449b82651accf78a10828b2c02b2f2d9e9496446c21", size = 2202822, upload-time = "2026-08-28T09:59:24.277Z" }, + { url = "https://files.pythonhosted.org/packages/31/b0/d16e0771206b29314f0d52198b720be21e8a99ab2bf11e3bc0d7c9cebdff/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64e88d5585bea9ce95861079de72006c7fa6d3df4e3a3b65ba31eb979c15c9f", size = 2262756, upload-time = "2026-08-28T09:59:26.608Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9b/59634b7ac631c63b2a37760eb6943af3e29573d6b59a4abc5e7f019d4cee/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d510bac3ee52247af28ed4bb18a1e799f040ac60fd2bf5ccd4c92f1fbe786f", size = 2068352, upload-time = "2026-08-28T09:59:29.044Z" }, + { url = "https://files.pythonhosted.org/packages/08/7c/570abb1ad2155348dc754ea91be22e5aaa18eb6d69a6068f7c6f2679a6ed/pydantic_core-2.46.5-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:a2a5e1d0ff29adddc9f6d6821a66302e4493f8ca898b715b6b1182c2c201ea0a", size = 2104777, upload-time = "2026-08-28T09:59:30.95Z" }, + { url = "https://files.pythonhosted.org/packages/8e/25/5bf74adc65a1ac5b7be3f6cb0bcb5433615c1598a801c19d830d84c98ded/pydantic_core-2.46.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03b9666e41e35d8909852ba191a0607520f81b74eaf12ccf8737005dbb313821", size = 2156312, upload-time = "2026-08-28T09:59:32.604Z" }, + { url = "https://files.pythonhosted.org/packages/90/6a/2ef38830675e050121040618135564ed56b860b45433b02d9b4ebece46f3/pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:a91c17edf6eea2402cb5457b4c89e99bc5ed1004aa34c4adf1d4258c1a5c22c2", size = 2150067, upload-time = "2026-08-28T09:59:34.453Z" }, + { url = "https://files.pythonhosted.org/packages/90/ef/a7dbb03a14a64c2a4621f989c615ed9a892535a6cad938fc27079f919d80/pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b49924c73a235e969511bf2aabdff3beebf9820931f646c80274d5d780010c47", size = 2304516, upload-time = "2026-08-28T09:59:36.194Z" }, + { url = "https://files.pythonhosted.org/packages/68/f8/6bb4c4b80e8a6fde1904c64a51c62a1d04fcdfa3ea521a66b2ddefa1d885/pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:2cbd9a5eff05e51c447c34dfa4632145b26b09120cf04bd0c871e44c1a5e1c9a", size = 2335223, upload-time = "2026-08-28T09:59:37.931Z" }, + { url = "https://files.pythonhosted.org/packages/2a/80/f46b8c681195190b2c1f1c7c0a81abce60663e987613e09ef64d433dd96b/pydantic_core-2.46.5-cp314-cp314-win32.whl", hash = "sha256:2d5d76654becf5efd62c9e51c3756c67b49498b0c9a40884934c40807adbd074", size = 1934827, upload-time = "2026-08-28T09:59:39.836Z" }, + { url = "https://files.pythonhosted.org/packages/f7/3c/60674207246bc0a4009d2391b7c7251c7159f279c8d2ab8aae8ef46f3dee/pydantic_core-2.46.5-cp314-cp314-win_amd64.whl", hash = "sha256:fa10ef4112775900e7a0661068635eb67b2ab824fbde764de6e0e21982a93db0", size = 2042648, upload-time = "2026-08-28T09:59:41.792Z" }, + { url = "https://files.pythonhosted.org/packages/69/0c/117c562c7c1babdf44576b72a5e496906506c93690387ecfbca7c729ae2e/pydantic_core-2.46.5-cp314-cp314-win_arm64.whl", hash = "sha256:045ab3b6d308439e32b81cc173bba5b9018bc6ed896afd0c65b3b009b1699af5", size = 1989652, upload-time = "2026-08-28T09:59:43.702Z" }, + { url = "https://files.pythonhosted.org/packages/e8/66/9336ae58f9eb68c41d121894e52c4c89eccb07eb8f602a04ee9c3f37736a/pydantic_core-2.46.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8816f3d218beb4b787de5c9759c259b8fa61f9dec42dc7811f320a33771778b7", size = 2065829, upload-time = "2026-08-28T09:59:45.364Z" }, + { url = "https://files.pythonhosted.org/packages/c5/02/bc19b47a96c2d3109760711acf22369e56bd7e405ca52f7ade164d2ead57/pydantic_core-2.46.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:bce57638e08ac148e5778cce7feb968307a727d66f8e2274a543d0cf0c9ad6a3", size = 1905716, upload-time = "2026-08-28T09:59:47.18Z" }, + { url = "https://files.pythonhosted.org/packages/52/a4/70b47c0509923dd98ccfed04fb3e32ea3849c82a0ff2205bb41009b43c00/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:976e1128455aa595ea04c79ccfedff1aaeab96ee013fcc916bed120c4f0ad94f", size = 1934216, upload-time = "2026-08-28T09:59:49.241Z" }, + { url = "https://files.pythonhosted.org/packages/52/ab/aa03b65f7bb198585edf806b906c3223ecf1795543e39e23aec4cce27ad2/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b891faeedeafba41b2983e5001a81b6a915b69544c7e7570d1989ce1c36ac7", size = 2010635, upload-time = "2026-08-28T09:59:51.692Z" }, + { url = "https://files.pythonhosted.org/packages/3c/8b/0da06343f30b84ec549aafd309c6456223d5dc8bd36af504c573faad561d/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f194189415698233dd1114a093a9b56e61e2c57e11b469be3b0506f46f0771c", size = 2209369, upload-time = "2026-08-28T09:59:53.582Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5b/844c4defaa34a3df66eb9257087d121d70c201298b96abdf9f492fc2f1bf/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82a36973cf8a2ef5406f4fe2edbf8ed0c99629535d959e0b100c76a32535a111", size = 2253238, upload-time = "2026-08-28T09:59:55.484Z" }, + { url = "https://files.pythonhosted.org/packages/f4/64/a4e536cb16d7f61a7fd3120b46c577fc7fa7325992f69c4f52bc786d77d8/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdbb78909f52b981d3b2d56b97328d71eb0b974c36bd77c920123a7ebb192829", size = 2065740, upload-time = "2026-08-28T09:59:58.038Z" }, + { url = "https://files.pythonhosted.org/packages/5f/75/aaa38c6bc2d085f6605b34eabdc6a8a4e0b2e61fc9c8e6e52b28e97b3125/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:52e24eacdb536cade636aa90fb851835222becff8484b7001fdc78cb0290f2aa", size = 2087425, upload-time = "2026-08-28T09:59:59.898Z" }, + { url = "https://files.pythonhosted.org/packages/55/ae/fcab4cfc39aba3689e1d20c8b5250ad280957022c09af2ed9cd585602a5e/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:37ae34309d7bd8c0d61ab839668058f2a7962ea1fc51d105d2db228fe0618034", size = 2139306, upload-time = "2026-08-28T10:00:03.057Z" }, + { url = "https://files.pythonhosted.org/packages/2d/f4/f1d03a4bc9d9acbc62f4d742b8a319af52f71885079868b2ff8e48a651ee/pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:0cdbada856a1c69a7624a64d3d9aefe79300bd6ef827b43a4f265010b9b55184", size = 2144589, upload-time = "2026-08-28T10:00:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/83/f3/7a53bb1356de514a4cd295f25b6ac39237895620c0462d2592b76c16e114/pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:545f26c504b27c3758439a5e6d9349931f0a04f855668d5fe323c89e82300a38", size = 2288882, upload-time = "2026-08-28T10:00:07.931Z" }, + { url = "https://files.pythonhosted.org/packages/cd/94/5a81583660c175c59d49ffb09f4b3a44debeaf86a19fca664ae1cdd9ee32/pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:ff218293c9c806138dca139765e3b067621be52bcd93cdc14c7711be7ddc90a9", size = 2335210, upload-time = "2026-08-28T10:00:10.177Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9f/5d685c2693b972d1a59c998586e8823712b66603aeff47ee60a4bdaafd37/pydantic_core-2.46.5-cp314-cp314t-win32.whl", hash = "sha256:97cf3eb53a8cccacf9d46686a0926186c9bfb5574f2ed66d3639d5fe117cd3a9", size = 1921180, upload-time = "2026-08-28T10:00:12.35Z" }, + { url = "https://files.pythonhosted.org/packages/70/12/5c94ee16d65a37a15f9e869f5e6256df111154491173801a4c5e800ab548/pydantic_core-2.46.5-cp314-cp314t-win_amd64.whl", hash = "sha256:d2f9fc07a8042a8f95925b35c4f04f469707c981fc33245b6ca187cf5d2dd290", size = 2020515, upload-time = "2026-08-28T10:00:14.774Z" }, + { url = "https://files.pythonhosted.org/packages/63/19/67830dda664e6bdf9285ee2e40f355d0d7d6b92aa0c42e8d217bb8d33d36/pydantic_core-2.46.5-cp314-cp314t-win_arm64.whl", hash = "sha256:acf8a67ba51f4ca9ddbd0e6b3000a65ac51ab734661778b3e7ba64d99a710f2f", size = 1989276, upload-time = "2026-08-28T10:00:16.984Z" }, ] [[package]] @@ -2672,7 +2669,7 @@ wheels = [ [[package]] name = "qdrant-client" -version = "1.19.0" +version = "1.19.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "grpcio" }, @@ -2683,9 +2680,9 @@ dependencies = [ { name = "pydantic" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/33/c6e4ec45b4fca5a0b808e8804e60be54a6d7505b68b53c0b1d0d62ba86c1/qdrant_client-1.19.0.tar.gz", hash = "sha256:365395a04b0a26c309b25b7d8b1c99ef2071ec9a2b74bc8a5fd3b7a3642fe963", size = 350953, upload-time = "2026-08-04T14:32:56.923Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/20/c8fcd645d3f595b086fa11a085980e9f641fd56fc6221fb325d634b8c4fa/qdrant_client-1.19.1.tar.gz", hash = "sha256:8f1d851a8463ce8cc11cf39ed8a9c9fb4b5f9de60e9a096ff56da42d1f074907", size = 360625, upload-time = "2026-09-16T06:43:13.818Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/3c/480c61cc8d5a3e76bb44e86231f408c93643e5498beadbbeb381ab55d02b/qdrant_client-1.19.0-py3-none-any.whl", hash = "sha256:13602a2b3478a95ecdf42f97b93d7f703b63a3361cd912a04495a33a5ac14121", size = 396157, upload-time = "2026-08-04T14:32:55.734Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9f/becebdda02beddd422587eba0d7dfac5b1f1e0aa1ada5bcf9b9e6f1c3717/qdrant_client-1.19.1-py3-none-any.whl", hash = "sha256:fca1a96c3f90f5fff853f6ee6877838a5768a04c963df9891a655a63313af8a0", size = 406533, upload-time = "2026-09-16T06:43:12.428Z" }, ] [[package]] @@ -2913,27 +2910,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.16.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/8f/d8074b1f25e003164087a8bfe79a0f1a3945135764dbb6aaab04103dcaf9/ruff-0.16.4.tar.gz", hash = "sha256:13171aa9d9af2240ee3504e639de73122c67e74036de5ba2e1d01422cd17e3dc", size = 4899731, upload-time = "2026-08-20T17:43:59.196Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/80/779895ef584e089d22f2c6df0d0e99a65ec2df0805f1fffd439415b8c1f0/ruff-0.16.4-py3-none-linux_armv6l.whl", hash = "sha256:df4075f71ddac40b9934af60c3ec8a53047dd5a5fdc43224e6e4e8e9a27cb6f7", size = 10006909, upload-time = "2026-08-20T17:43:16.888Z" }, - { url = "https://files.pythonhosted.org/packages/a9/e6/f553199b5e8927a05cb5c422d921fd0656b29ab976e91c44802107c6b0da/ruff-0.16.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:0c95538517af68004306b0fb3214ff2f2af67a65092aee77cd9eb86db6656604", size = 10240201, upload-time = "2026-08-20T17:43:19.337Z" }, - { url = "https://files.pythonhosted.org/packages/1c/70/4a6dc4bb34da4dee35e30f09bbd1bfbdd26f33b62fb9b8df31f08a199cd2/ruff-0.16.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:963f83df8e69e575b64d67dd447ebbc917db41a14bf38d4593a4183e7aaa8255", size = 9835122, upload-time = "2026-08-20T17:43:21.708Z" }, - { url = "https://files.pythonhosted.org/packages/24/12/c6e22d686372c15bcb7af99831f1a1be96df696491babf4f24e4f942c527/ruff-0.16.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32a5057c7ff3f6e6480a48fccfb3a412a690f48a3d03ac5cf08177d6c2da3ade", size = 9977162, upload-time = "2026-08-20T17:43:24.236Z" }, - { url = "https://files.pythonhosted.org/packages/46/49/72b10ec912f5ab5854992eaf7aa7cd36729b6937d9dc4e0fb41b3bf428ec/ruff-0.16.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b3dce8d9b0c57c265b91885a66a567d8ea1372e8eb4e250fa8e5e3f579e99cff", size = 9829789, upload-time = "2026-08-20T17:43:26.966Z" }, - { url = "https://files.pythonhosted.org/packages/fa/80/0f30e32e7f6ee26edc39075502db9d368d788a44a79b55f763eb4ab03796/ruff-0.16.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7dc651db49283c69f8e72c834eec4fe5573e4c646856aebece0ce385dceb2a80", size = 10527949, upload-time = "2026-08-20T17:43:29.384Z" }, - { url = "https://files.pythonhosted.org/packages/52/3d/86e8ad3542169e56cac3859a343afdb9df2ad54d35a59ce1e67baee83421/ruff-0.16.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3817b87dbcabc92f13b05019257c5b89b5b4d51b5fb20f56fb5235ceb723cd07", size = 11333695, upload-time = "2026-08-20T17:43:31.872Z" }, - { url = "https://files.pythonhosted.org/packages/d0/16/481c29b380c20a0054a8261066665e1b3488e23636c49d0a43e75975b9bb/ruff-0.16.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9fce1499134b2c8c68e5166f95705a5812062bb93aacc5f9873bb1a27084bc7", size = 10727741, upload-time = "2026-08-20T17:43:34.596Z" }, - { url = "https://files.pythonhosted.org/packages/5e/b6/56bc0b8cf45b54b28b3a5e6381c8945d51b5b18adf659454c32295209a31/ruff-0.16.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2d812e482f5a7e02eee26cd73d2a37ebbdf47d795ea63ba1b89110ae93e9fb3", size = 10286522, upload-time = "2026-08-20T17:43:37.288Z" }, - { url = "https://files.pythonhosted.org/packages/e8/8b/b345b4fb110f2fbe2bd31eabd271e5e8b3b7e4ee6c0e02f2dc6be78db000/ruff-0.16.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:6baaf984aa7976edf93d3b627fe2d1d22ee94bbca05fa6f90fc76d73924e3454", size = 10584182, upload-time = "2026-08-20T17:43:39.984Z" }, - { url = "https://files.pythonhosted.org/packages/29/e5/827b34041c35f58774a9681a4213994c164fc987800f4dddabcf451da0bf/ruff-0.16.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:bdfcf0b28662eb890372d50f92c283bb94e67e7635ed93c7fd533970acff7b2b", size = 10134195, upload-time = "2026-08-20T17:43:42.351Z" }, - { url = "https://files.pythonhosted.org/packages/0f/10/d0bffcdd6729b87afc82ba0ef377173356a7dc8e972f5179968cf2fdf98c/ruff-0.16.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b66b02cb9b04f537643cadf5768e5f98dc461890d530cb67113d71c8c76e605d", size = 9825821, upload-time = "2026-08-20T17:43:44.532Z" }, - { url = "https://files.pythonhosted.org/packages/f5/32/0db2a863b796ca62d83e92a07a3ccf00921b14db02059347576a2fda3d4b/ruff-0.16.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:8528bf9a4b291a60bf02ea453511e8ce6215bd2b982ee80405b66b008b6c30a0", size = 10267658, upload-time = "2026-08-20T17:43:46.989Z" }, - { url = "https://files.pythonhosted.org/packages/b2/a0/fbdeb59e48c6261f523e56c8f12e9c08fbe693786595cc7e3959207a9232/ruff-0.16.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:fbd85d2875fdd67e833213a651f613bbf25303abf6aa822a5121f4531195678d", size = 10697071, upload-time = "2026-08-20T17:43:49.891Z" }, - { url = "https://files.pythonhosted.org/packages/aa/28/0c6dd865859c6d17bc8ccc34cb72b0e02d6c7eb25e8a1e22b5bea681e2c0/ruff-0.16.4-py3-none-win32.whl", hash = "sha256:312769988007aaeb8e189b443ccdd03c0e6374489e053467be6d96518ebff76e", size = 10021687, upload-time = "2026-08-20T17:43:52.281Z" }, - { url = "https://files.pythonhosted.org/packages/a3/03/e724450f621698117f9aa6dd241c94d0274ae96781378dc86745ae29f0e7/ruff-0.16.4-py3-none-win_amd64.whl", hash = "sha256:05d9d27a18c4bcbefada602480ec9e01e0bc949d432e0ced5df77edac195919c", size = 10567657, upload-time = "2026-08-20T17:43:54.78Z" }, - { url = "https://files.pythonhosted.org/packages/0e/fe/da8b9e1347696bb22120b77280ec5ce25d500ca5cb39d5ad6e5c18de19c1/ruff-0.16.4-py3-none-win_arm64.whl", hash = "sha256:a3a61621c9b6f6a89573e938a080e648f1695baa3f58570a3a707bc51ff65a21", size = 10451579, upload-time = "2026-08-20T17:43:57.135Z" }, +version = "0.16.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/78/449cb84790bd5cc3823b2652ee405a4558856e5c4195aee3a16bf7b3eb5d/ruff-0.16.8.tar.gz", hash = "sha256:9247bf92b5f04d825c8639a4fe423ec2e4222acd9222e58412b0dab7e442798b", size = 4938814, upload-time = "2026-09-16T15:54:46.688Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/25/6071aabc530e9be7e2c195e8fe3f7aea2735405b6cf447212832d7811831/ruff-0.16.8-py3-none-linux_armv6l.whl", hash = "sha256:6ffbd6d87383c1edf5f6fa890f10200950240d7c1a16052a19a09d3a2307dd38", size = 10048966, upload-time = "2026-09-16T15:53:57.605Z" }, + { url = "https://files.pythonhosted.org/packages/54/98/07f90ecbc74dd5fb5764f11f2bc774d6a7cffef92d2ff5f5b4e9e23c754e/ruff-0.16.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:42ed6b878ed61e3acca92f2730a17acff39286944ea82398544696366a6f925e", size = 10165498, upload-time = "2026-09-16T15:54:01.14Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1f/e6a712e3b47cad4a40600134105ed193cb773f618a42eb7ba323cb812cc0/ruff-0.16.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7ea781c7f2afba8c6a505ea0fb3f994020249e0c450635f5381286fea6b46170", size = 9830004, upload-time = "2026-09-16T15:54:03.998Z" }, + { url = "https://files.pythonhosted.org/packages/23/f2/311a08776d75d81c7676e20b6b020ae63cbe881fcdc7a8dd64e6e18bdd93/ruff-0.16.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8efeae3bbe414a5efefda11a792dfb51ef90ac48d50c4830de2f644caf3e8659", size = 9986558, upload-time = "2026-09-16T15:54:06.804Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ed/37b6cb3d3ba8c73e68ae3eb1d502383beb5aa05a582bb7bb3a922f929f54/ruff-0.16.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a79b795469fef7fc6e908b218eed2eb17332afd85031db6480dc864560e69b2", size = 9877332, upload-time = "2026-09-16T15:54:09.552Z" }, + { url = "https://files.pythonhosted.org/packages/22/cc/40873a8f36ad084cc540d55fcca7077264d5b13b24659e9180c176fb2b08/ruff-0.16.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fdc5563cdc50555e6fba39322850860e9267c1b3d12c26a74729d8604c3c812", size = 10507125, upload-time = "2026-09-16T15:54:12.152Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e4/fc91a642b78ccbab6b9477720f3644ae7a10a9bcce69a934679cd64f62bc/ruff-0.16.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34508983c70665578dab88f5223d8e6228307e1135398ca8bfc8b7e9501e282b", size = 11336694, upload-time = "2026-09-16T15:54:15.489Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3d/bbd2a9a600a4e73dc3e7548a249c8d1671273464b55822c6fae50f602dff/ruff-0.16.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:644bb578569e0ffc575741232bd385dacdd6fbe123f1a729e7a225f54aa3957f", size = 10774448, upload-time = "2026-09-16T15:54:18.16Z" }, + { url = "https://files.pythonhosted.org/packages/1a/41/d83af9879a7b6e8bf5fe16b1da0b134049d2f5d3afac12defb0897cb84bd/ruff-0.16.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15e7d226246961db9235098333caa13063906d3851136b84c2900b82f5daa1df", size = 10323796, upload-time = "2026-09-16T15:54:20.743Z" }, + { url = "https://files.pythonhosted.org/packages/f5/2c/cefd07bfe914b84943ea769ade8d607bd22750b965d3228eefd7cebd15d0/ruff-0.16.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a2bf6bc3e9ebdd4449abc6f06cf64b98051a2c61cf94d2fe9596518c881f1a1e", size = 10514115, upload-time = "2026-09-16T15:54:23.497Z" }, + { url = "https://files.pythonhosted.org/packages/f3/9d/76a2e26c79a23be6e6e3664c57bec9e9fc8de155cfb9e4b67ea91b64f9d7/ruff-0.16.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ca111ba0849539165e9e59d2b442542f3c1e8060ebbdea82494f1ffbccb1e1f", size = 10072582, upload-time = "2026-09-16T15:54:26.185Z" }, + { url = "https://files.pythonhosted.org/packages/2e/d4/f42edddb39668af1a559ceafa3823aedd65633a48dc9768e775485faa2c1/ruff-0.16.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:359a1e5b495448ee1e91018064382ebc86f90e8aac2fed222c7d0e4e8df85fd2", size = 9879644, upload-time = "2026-09-16T15:54:29.278Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/913e3195d95e0378786c6656945c865f534a3560e29139da4882aff630d1/ruff-0.16.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:59e8f5681349474110b24d62e93cfda6593f5fa3473446ca3705200cac1a08b9", size = 10231569, upload-time = "2026-09-16T15:54:32.036Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c4/8aa6ea0bdcedbd1bf87397e2fc4ed8406448ea5842f8660bc6e5f163039d/ruff-0.16.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:efa3e7a16d1baaa79957888dfdf8be9ef2e44db81cb032af06d76632ab59e773", size = 10663666, upload-time = "2026-09-16T15:54:34.838Z" }, + { url = "https://files.pythonhosted.org/packages/3d/02/7f10ef4700bc223c30a3fdd10631a29830c45524b810a3c7ed947af64591/ruff-0.16.8-py3-none-win32.whl", hash = "sha256:55793ba85c69921e89be061426d91a78652d6e50317c962240922747a4eb713f", size = 10093472, upload-time = "2026-09-16T15:54:37.47Z" }, + { url = "https://files.pythonhosted.org/packages/1e/5d/a509c07d714b6da88f2c518b4637cf6f1d46b074be8f0f1e5fb9ff5126fe/ruff-0.16.8-py3-none-win_amd64.whl", hash = "sha256:a6b85621fd3c81e31fc5f5add09c9c078b430db3595ca632efafdec9e64ebfaa", size = 10586899, upload-time = "2026-09-16T15:54:40.488Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a0/50787329e4f20bf9dc9f6230015d46ec69c51a97ace5bc202dae4755365d/ruff-0.16.8-py3-none-win_arm64.whl", hash = "sha256:d075e820af612102ce217f07cc93e69f9490b10ec13ea85fa87bd03d996cef8a", size = 10386316, upload-time = "2026-09-16T15:54:43.332Z" }, ] [[package]] @@ -3111,7 +3108,7 @@ wheels = [ [[package]] name = "typer" -version = "0.27.1" +version = "0.27.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -3119,18 +3116,18 @@ dependencies = [ { name = "rich" }, { name = "shellingham" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/40/4a3db7990d1f62a53182aa96eaef57aeb2886a27f90a195bc66713565d31/typer-0.27.1.tar.gz", hash = "sha256:a79bef8469a79c45498e7b814ecf8d603cc7644e9acbd9e19cac0334240b18df", size = 203994, upload-time = "2026-08-03T14:41:03.438Z" } +sdist = { url = "https://files.pythonhosted.org/packages/16/f7/57713ba479fd405eb76de31404b2c744c289e336b2d999511ebf51e496f7/typer-0.27.2.tar.gz", hash = "sha256:269b7eb9d3c202ca84b4bc9618cb04ebb43d3d4d1e567e4c768607232c05f945", size = 204045, upload-time = "2026-08-28T10:26:55.046Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/89/9518bc0c3929bee36b3a4a8e3daddd6e03f92f9961c66d4983b837160543/typer-0.27.1-py3-none-any.whl", hash = "sha256:53150287edd11baeb4e4722c8e394fcdf8181c0ae89485cba8d25c778d5edd56", size = 122874, upload-time = "2026-08-03T14:41:04.391Z" }, + { url = "https://files.pythonhosted.org/packages/dc/bf/205d0004930ede8f542fb58f601526fccf4ae7626075ca1e6c4de5d3d652/typer-0.27.2-py3-none-any.whl", hash = "sha256:b3a5fc4342d5fc8fda8fc3010b1cf117e9249aab7fae800c2eff62fd3842d97d", size = 123130, upload-time = "2026-08-28T10:26:53.752Z" }, ] [[package]] name = "types-pyyaml" -version = "6.0.12.20260815" +version = "6.0.12.20260906" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9f/72/b56089aeee6c496d969bac42376bedb6e3eeab4682e1018fa3137122f94b/types_pyyaml-6.0.12.20260815.tar.gz", hash = "sha256:28764110c9cf35846e733da32d8d734df7473c5dde9ef67c3b7332ec0e819858", size = 18545, upload-time = "2026-08-15T02:41:51.532Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/6e/abec85b9013db5b934b0280a6dd104904d84f7bcbaab2e2f3def87ac7463/types_pyyaml-6.0.12.20260906.tar.gz", hash = "sha256:f59c1cc05010b833d2d72287bbaa72610106b28d42d89a907313117faba85212", size = 18649, upload-time = "2026-09-06T06:35:35.362Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/52/eefeba09be4ef2a1eb989eb92934561e8e502a6ee3c32654996e4be7e399/types_pyyaml-6.0.12.20260815-py3-none-any.whl", hash = "sha256:6f332212b7e191f3afd5016a713c510b6340593b7ebec573c7d5d20aa5386d3b", size = 21148, upload-time = "2026-08-15T02:41:50.555Z" }, + { url = "https://files.pythonhosted.org/packages/15/c0/fc0644b7ddcfb969e95845837143cb5173ddd6e06ee4ba5fc493cd9329b7/types_pyyaml-6.0.12.20260906-py3-none-any.whl", hash = "sha256:bca893ff0d51df5c9053137d5d0e6ccd36e939a196356f1d5c16372422f5137b", size = 21282, upload-time = "2026-09-06T06:35:34.372Z" }, ] [[package]] @@ -3174,15 +3171,15 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.52.4" +version = "0.53.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f2/0f/3f86e61397dd33bf2ccf28188c40db6a740658aeebbbf6e7dbc101a1f487/uvicorn-0.52.4.tar.gz", hash = "sha256:73acfee47a0b133c5de13d219492d62d8a31e935f4fe6e41a232451a15379f86", size = 100627, upload-time = "2026-08-19T06:27:41.821Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/ad/04bbb797c84fc1f26cb171f7394716f4865ffb8d8c5e1eef42565c2dfa6b/uvicorn-0.53.0.tar.gz", hash = "sha256:a9356f0cb89b3b8621529c5d5eebd69bfe154f4c3f68b4cf2de47e45fa855c2e", size = 110881, upload-time = "2026-09-14T07:44:23.815Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/79/4a20b54ab0491485ccd8c077db2d39187c7f12b3e15485d38a7be37c81b4/uvicorn-0.52.4-py3-none-any.whl", hash = "sha256:f86e41a149d7d05a9969337e3946a9c171c06a5d42680896daaba624aeac8da1", size = 79871, upload-time = "2026-08-19T06:27:40.36Z" }, + { url = "https://files.pythonhosted.org/packages/76/18/0eea75741ee812e9f598b687619ce2454f6c3a1c5cd21ea990ec6bd26f45/uvicorn-0.53.0-py3-none-any.whl", hash = "sha256:e8dca71ec86dce5f04e333f0d56cdedf942446e6643b9cea1af0d6d3a02cb03e", size = 87081, upload-time = "2026-09-14T07:44:22.179Z" }, ] [[package]]