smooai-audit 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- smooai_audit-0.1.0/.gitignore +63 -0
- smooai_audit-0.1.0/.python-version +1 -0
- smooai_audit-0.1.0/PKG-INFO +55 -0
- smooai_audit-0.1.0/README.md +36 -0
- smooai_audit-0.1.0/basedpyrightconfig.json +14 -0
- smooai_audit-0.1.0/pyproject.toml +71 -0
- smooai_audit-0.1.0/pyrightconfig.json +15 -0
- smooai_audit-0.1.0/src/smooai_audit/__init__.py +29 -0
- smooai_audit-0.1.0/src/smooai_audit/canonical.py +83 -0
- smooai_audit-0.1.0/src/smooai_audit/client.py +68 -0
- smooai_audit-0.1.0/src/smooai_audit/hash.py +48 -0
- smooai_audit-0.1.0/src/smooai_audit/py.typed +0 -0
- smooai_audit-0.1.0/src/smooai_audit/schema.py +127 -0
- smooai_audit-0.1.0/tests/test_audit.py +113 -0
- smooai_audit-0.1.0/typings/.gitkeep +0 -0
- smooai_audit-0.1.0/uv.lock +332 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
lib-cov
|
|
2
|
+
*.seed
|
|
3
|
+
*.log
|
|
4
|
+
*.csv
|
|
5
|
+
*.dat
|
|
6
|
+
*.out
|
|
7
|
+
*.pid
|
|
8
|
+
*.gz
|
|
9
|
+
*.swp
|
|
10
|
+
|
|
11
|
+
pids
|
|
12
|
+
logs
|
|
13
|
+
results
|
|
14
|
+
tmp
|
|
15
|
+
|
|
16
|
+
# Build
|
|
17
|
+
public/css/main.css
|
|
18
|
+
|
|
19
|
+
# Coverage reports
|
|
20
|
+
coverage
|
|
21
|
+
|
|
22
|
+
# API keys and secrets
|
|
23
|
+
.env
|
|
24
|
+
|
|
25
|
+
# Dependency directory
|
|
26
|
+
node_modules
|
|
27
|
+
bower_components
|
|
28
|
+
|
|
29
|
+
# Editors
|
|
30
|
+
.idea
|
|
31
|
+
*.iml
|
|
32
|
+
|
|
33
|
+
# OS metadata
|
|
34
|
+
.DS_Store
|
|
35
|
+
Thumbs.db
|
|
36
|
+
|
|
37
|
+
# Ignore built ts files
|
|
38
|
+
dist/**/*
|
|
39
|
+
|
|
40
|
+
# ignore yarn.lock
|
|
41
|
+
yarn.lock
|
|
42
|
+
|
|
43
|
+
# ignore package-lock.json
|
|
44
|
+
package-lock.json
|
|
45
|
+
|
|
46
|
+
.smooai-logs
|
|
47
|
+
|
|
48
|
+
.envrc
|
|
49
|
+
# Python build artifacts
|
|
50
|
+
python/.venv/
|
|
51
|
+
python/dist/
|
|
52
|
+
python/.pytest_cache/
|
|
53
|
+
python/**/__pycache__/
|
|
54
|
+
|
|
55
|
+
# Rust build artifacts
|
|
56
|
+
rust/audit/target/
|
|
57
|
+
|
|
58
|
+
# .NET build artifacts
|
|
59
|
+
dotnet/**/bin/
|
|
60
|
+
dotnet/**/obj/
|
|
61
|
+
dotnet/dist/
|
|
62
|
+
*.nupkg
|
|
63
|
+
*.snupkg
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: smooai-audit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Polyglot client SDK for tamper-evident, SQL-queryable audit logging — Python port of @smooai/audit.
|
|
5
|
+
Project-URL: Homepage, https://github.com/SmooAI/audit
|
|
6
|
+
Project-URL: Repository, https://github.com/SmooAI/audit
|
|
7
|
+
Project-URL: Issues, https://github.com/SmooAI/audit/issues
|
|
8
|
+
Author-email: SmooAI <brent@smooai.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: audit,audit-log,hash-chain,smooai,tamper-evident
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.12
|
|
17
|
+
Requires-Dist: pydantic>=2.7.0
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# smooai-audit (Python)
|
|
21
|
+
|
|
22
|
+
Python port of [`@smooai/audit`](https://github.com/SmooAI/audit) — a polyglot
|
|
23
|
+
client SDK for tamper-evident, SQL-queryable audit logging: a canonical
|
|
24
|
+
`AuditEvent` schema, canonical JSON serialization, a per-org-per-day SHA-256 hash
|
|
25
|
+
chain, and an emit client. Verified byte-for-byte against a shared parity corpus.
|
|
26
|
+
|
|
27
|
+
> **Status:** scaffold — `canonical_json`, `compute_event_hash`, and
|
|
28
|
+
> `AuditClient.emit` are stubbed (`TODO(audit-impl)`).
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
uv add smooai-audit # or: pip install smooai-audit
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from smooai_audit import AuditClient, AuditClientOptions, AuditEvent
|
|
40
|
+
|
|
41
|
+
client = AuditClient(AuditClientOptions(endpoint=endpoint, token=token))
|
|
42
|
+
client.emit(
|
|
43
|
+
AuditEvent(
|
|
44
|
+
id="...",
|
|
45
|
+
org_id="org_123",
|
|
46
|
+
timestamp="2026-07-14T00:00:00.000Z",
|
|
47
|
+
actor="user_abc",
|
|
48
|
+
action="record.delete",
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
MIT © SmooAI
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# smooai-audit (Python)
|
|
2
|
+
|
|
3
|
+
Python port of [`@smooai/audit`](https://github.com/SmooAI/audit) — a polyglot
|
|
4
|
+
client SDK for tamper-evident, SQL-queryable audit logging: a canonical
|
|
5
|
+
`AuditEvent` schema, canonical JSON serialization, a per-org-per-day SHA-256 hash
|
|
6
|
+
chain, and an emit client. Verified byte-for-byte against a shared parity corpus.
|
|
7
|
+
|
|
8
|
+
> **Status:** scaffold — `canonical_json`, `compute_event_hash`, and
|
|
9
|
+
> `AuditClient.emit` are stubbed (`TODO(audit-impl)`).
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv add smooai-audit # or: pip install smooai-audit
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from smooai_audit import AuditClient, AuditClientOptions, AuditEvent
|
|
21
|
+
|
|
22
|
+
client = AuditClient(AuditClientOptions(endpoint=endpoint, token=token))
|
|
23
|
+
client.emit(
|
|
24
|
+
AuditEvent(
|
|
25
|
+
id="...",
|
|
26
|
+
org_id="org_123",
|
|
27
|
+
timestamp="2026-07-14T00:00:00.000Z",
|
|
28
|
+
actor="user_abc",
|
|
29
|
+
action="record.delete",
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## License
|
|
35
|
+
|
|
36
|
+
MIT © SmooAI
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"include": ["src/**/*.py", "tests/**/*.py"],
|
|
3
|
+
"extraPaths": ["./src"],
|
|
4
|
+
"pythonVersion": "3.12",
|
|
5
|
+
"typeCheckingMode": "standard",
|
|
6
|
+
"reportUnusedCallResult": false,
|
|
7
|
+
"reportExplicitAny": false,
|
|
8
|
+
"reportAny": false,
|
|
9
|
+
"reportUnknownMemberType": false,
|
|
10
|
+
"reportUntypedFunctionDecorator": false,
|
|
11
|
+
"reportUnnecessaryIsInstance": false,
|
|
12
|
+
"reportMissingTypeStubs": false,
|
|
13
|
+
"reportUnnecessaryTypeIgnoreComment": false
|
|
14
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "smooai-audit"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Polyglot client SDK for tamper-evident, SQL-queryable audit logging — Python port of @smooai/audit."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [{ name = "SmooAI", email = "brent@smooai.com" }]
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
requires-python = ">=3.12"
|
|
9
|
+
dependencies = ["pydantic>=2.7.0"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Programming Language :: Python",
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"Programming Language :: Python :: 3.12",
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
]
|
|
17
|
+
keywords = ["audit", "audit-log", "tamper-evident", "hash-chain", "smooai"]
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Homepage = "https://github.com/SmooAI/audit"
|
|
21
|
+
Repository = "https://github.com/SmooAI/audit"
|
|
22
|
+
Issues = "https://github.com/SmooAI/audit/issues"
|
|
23
|
+
|
|
24
|
+
[build-system]
|
|
25
|
+
requires = ["hatchling"]
|
|
26
|
+
build-backend = "hatchling.build"
|
|
27
|
+
|
|
28
|
+
[dependency-groups]
|
|
29
|
+
dev = ["poethepoet>=0.29.0", "pytest>=8.3.0", "ruff>=0.11.6,<0.12", "basedpyright>=1.29.2"]
|
|
30
|
+
|
|
31
|
+
[tool.poe.tasks.install-dev]
|
|
32
|
+
cmd = "uv sync --locked --group dev"
|
|
33
|
+
|
|
34
|
+
[tool.poe.tasks.build]
|
|
35
|
+
cmd = "uv build --wheel --sdist"
|
|
36
|
+
|
|
37
|
+
[tool.poe.tasks.publish]
|
|
38
|
+
sequence = [
|
|
39
|
+
{ shell = "rm -rf dist/" },
|
|
40
|
+
{ cmd = "uv build --wheel --sdist" },
|
|
41
|
+
{ cmd = "uv publish" },
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[tool.poe.tasks.typecheck]
|
|
45
|
+
cmd = "basedpyright"
|
|
46
|
+
|
|
47
|
+
[tool.poe.tasks.typecheck-errors]
|
|
48
|
+
cmd = "basedpyright --level error"
|
|
49
|
+
|
|
50
|
+
[tool.poe.tasks.format]
|
|
51
|
+
cmd = "ruff format"
|
|
52
|
+
|
|
53
|
+
[tool.poe.tasks.lint]
|
|
54
|
+
cmd = "ruff check"
|
|
55
|
+
|
|
56
|
+
[tool.poe.tasks."lint:fix"]
|
|
57
|
+
cmd = "ruff check --fix"
|
|
58
|
+
|
|
59
|
+
[tool.poe.tasks.test]
|
|
60
|
+
cmd = "uv run pytest"
|
|
61
|
+
|
|
62
|
+
[tool.pytest.ini_options]
|
|
63
|
+
addopts = "-ra"
|
|
64
|
+
testpaths = ["tests"]
|
|
65
|
+
|
|
66
|
+
[tool.ruff]
|
|
67
|
+
line-length = 160
|
|
68
|
+
target-version = "py312"
|
|
69
|
+
|
|
70
|
+
[tool.ruff.lint]
|
|
71
|
+
select = ["E", "F", "I", "N", "W", "UP"]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"include": ["src/**/*.py"],
|
|
3
|
+
"extraPaths": ["./src/**"],
|
|
4
|
+
"stubPath": "./typings",
|
|
5
|
+
"pythonVersion": "3.13",
|
|
6
|
+
"failOnWarnings": false,
|
|
7
|
+
"reportUnusedCallResult": false,
|
|
8
|
+
"reportExplicitAny": false,
|
|
9
|
+
"reportAny": false,
|
|
10
|
+
"reportUnknownMemberType": false,
|
|
11
|
+
"reportUntypedFunctionDecorator": false,
|
|
12
|
+
"reportUnnecessaryIsInstance": false,
|
|
13
|
+
"reportMissingTypeStubs": false,
|
|
14
|
+
"reportUnnecessaryTypeIgnoreComment": false
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Python client SDK for tamper-evident, SQL-queryable audit logging."""
|
|
2
|
+
|
|
3
|
+
from .canonical import canonical_json
|
|
4
|
+
from .client import AuditClient, AuditClientOptions
|
|
5
|
+
from .hash import build_hash_chain, compute_event_hash
|
|
6
|
+
from .schema import (
|
|
7
|
+
AUDIT_ACTIONS,
|
|
8
|
+
AuditActorType,
|
|
9
|
+
AuditDiff,
|
|
10
|
+
AuditEvent,
|
|
11
|
+
AuditOutcome,
|
|
12
|
+
AuditResource,
|
|
13
|
+
is_namespaced_action,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"AUDIT_ACTIONS",
|
|
18
|
+
"AuditActorType",
|
|
19
|
+
"AuditClient",
|
|
20
|
+
"AuditClientOptions",
|
|
21
|
+
"AuditDiff",
|
|
22
|
+
"AuditEvent",
|
|
23
|
+
"AuditOutcome",
|
|
24
|
+
"AuditResource",
|
|
25
|
+
"build_hash_chain",
|
|
26
|
+
"canonical_json",
|
|
27
|
+
"compute_event_hash",
|
|
28
|
+
"is_namespaced_action",
|
|
29
|
+
]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Canonical JSON serialization for audit events.
|
|
2
|
+
|
|
3
|
+
Reproduces JavaScript ``JSON.stringify`` semantics BYTE-FOR-BYTE (not "some
|
|
4
|
+
canonical JSON"), because the audit hash chain is
|
|
5
|
+
``hashCurrent = SHA-256(canonical-JSON(event minus hashCurrent))`` and a single
|
|
6
|
+
divergent byte breaks every downstream hash. See ``hash-chain.ts`` /
|
|
7
|
+
``canonical.rs`` for the sibling implementations this must match.
|
|
8
|
+
|
|
9
|
+
The three JS-vs-Python footguns handled here:
|
|
10
|
+
|
|
11
|
+
1. **String escaping** — JS escapes ``"`` and ``\\``, renders control chars
|
|
12
|
+
U+0000–U+001F as ``\\b \\t \\n \\f \\r`` or ``\\uXXXX``, does NOT escape the
|
|
13
|
+
forward slash, and emits all other chars (incl. non-ASCII) as literal UTF-8.
|
|
14
|
+
``json.dumps(s, ensure_ascii=False)`` matches this exactly, so we reuse it
|
|
15
|
+
rather than hand-roll an escaper.
|
|
16
|
+
2. **Number formatting** — integers render plain; ``bool`` is a subclass of
|
|
17
|
+
``int`` in Python so it MUST be checked first; non-finite floats become
|
|
18
|
+
``null`` like ``JSON.stringify``.
|
|
19
|
+
3. **Key ordering** — JS ``Array.prototype.sort`` orders by UTF-16 code units,
|
|
20
|
+
NOT Unicode code points. We sort by ``key.encode("utf-16-be")`` so an
|
|
21
|
+
astral-plane key sorts the same as it would in V8. (Field names are ASCII,
|
|
22
|
+
where the two orders agree, but the corpus mandates code-unit correctness.)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import json
|
|
28
|
+
import math
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
from pydantic import BaseModel
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _json_string(text: str) -> str:
|
|
35
|
+
# json.dumps with these settings matches JSON.stringify's string escaping:
|
|
36
|
+
# quote/backslash escaped, control chars as \b\t\n\f\r or \uXXXX, forward
|
|
37
|
+
# slash NOT escaped, non-ASCII emitted as literal UTF-8.
|
|
38
|
+
return json.dumps(text, ensure_ascii=False)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _format_number(value: int | float) -> str:
|
|
42
|
+
# bool is a subclass of int — callers must dispatch bool before reaching here.
|
|
43
|
+
if isinstance(value, int):
|
|
44
|
+
return str(value)
|
|
45
|
+
if not math.isfinite(value):
|
|
46
|
+
return "null" # JSON.stringify(NaN | Infinity) === "null"
|
|
47
|
+
if value.is_integer():
|
|
48
|
+
return str(int(value)) # JS has no int/float split: 5.0 → "5"
|
|
49
|
+
# ponytail: repr() gives shortest round-trip like V8 for typical decimals;
|
|
50
|
+
# exponent-form floats (e.g. 1e-7 → "1e-07" here vs "1e-7" in JS) can differ.
|
|
51
|
+
# Audit metadata is integers/strings in practice; widen if a float appears.
|
|
52
|
+
return repr(value)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def canonical_json(value: Any) -> str:
|
|
56
|
+
"""Serialize ``value`` to its canonical JSON string, byte-matching JS
|
|
57
|
+
``canonicalJsonStringify``: object keys sorted at every depth, array order
|
|
58
|
+
preserved, no insignificant whitespace.
|
|
59
|
+
|
|
60
|
+
Accepts raw JSON-compatible Python values (dict/list/str/int/float/bool/None)
|
|
61
|
+
or a pydantic model (dumped to its camelCase wire dict first). A PRESENT
|
|
62
|
+
``None`` renders as ``"null"``; only keys ABSENT from the mapping disappear
|
|
63
|
+
(the JS ``undefined`` rule — an absent optional was never in the dict)."""
|
|
64
|
+
if isinstance(value, BaseModel):
|
|
65
|
+
value = value.model_dump(by_alias=True, exclude_unset=True, mode="json")
|
|
66
|
+
|
|
67
|
+
if value is None:
|
|
68
|
+
return "null"
|
|
69
|
+
if isinstance(value, bool):
|
|
70
|
+
return "true" if value else "false"
|
|
71
|
+
if isinstance(value, int | float):
|
|
72
|
+
return _format_number(value)
|
|
73
|
+
if isinstance(value, str):
|
|
74
|
+
return _json_string(value)
|
|
75
|
+
if isinstance(value, list | tuple):
|
|
76
|
+
items: list[Any] = list(value)
|
|
77
|
+
return "[" + ",".join(canonical_json(item) for item in items) + "]"
|
|
78
|
+
if isinstance(value, dict):
|
|
79
|
+
obj: dict[str, Any] = value
|
|
80
|
+
keys = sorted(obj.keys(), key=lambda k: str(k).encode("utf-16-be"))
|
|
81
|
+
parts = (_json_string(str(k)) + ":" + canonical_json(obj[k]) for k in keys)
|
|
82
|
+
return "{" + ",".join(parts) + "}"
|
|
83
|
+
raise TypeError(f"canonical_json cannot serialize value of type {type(value).__name__}")
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Audit emit client (Python port of ``@smooai/audit``).
|
|
2
|
+
|
|
3
|
+
Seals an event (computes its ``hashCurrent``) and POSTs the canonical JSON to a
|
|
4
|
+
configurable ingest endpoint with a Bearer token. Uses the standard library
|
|
5
|
+
(``urllib``) — no HTTP dependency — since a single fire-and-forget POST needs no
|
|
6
|
+
more. Like the TS emitter, errors are swallowed by default: audit logging must
|
|
7
|
+
never break the calling code path.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import urllib.error
|
|
13
|
+
import urllib.request
|
|
14
|
+
from collections.abc import Callable
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
|
|
17
|
+
from .canonical import canonical_json
|
|
18
|
+
from .hash import compute_event_hash
|
|
19
|
+
from .schema import AuditEvent
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class AuditClientOptions:
|
|
24
|
+
"""Configuration for :class:`AuditClient`."""
|
|
25
|
+
|
|
26
|
+
endpoint: str
|
|
27
|
+
"""Ingest endpoint URL events are POSTed to."""
|
|
28
|
+
token: str
|
|
29
|
+
"""Bearer token used to authenticate emit requests."""
|
|
30
|
+
timeout: float = 10.0
|
|
31
|
+
"""Per-request timeout in seconds."""
|
|
32
|
+
swallow_errors: bool = True
|
|
33
|
+
"""Swallow transport errors silently (default). Set False to raise."""
|
|
34
|
+
on_error: Callable[[Exception], None] | None = None
|
|
35
|
+
"""Optional hook invoked with any swallowed error."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class AuditClient:
|
|
39
|
+
"""Emits audit events to a configurable ingest endpoint over HTTPS."""
|
|
40
|
+
|
|
41
|
+
def __init__(self, options: AuditClientOptions) -> None:
|
|
42
|
+
self._options = options
|
|
43
|
+
|
|
44
|
+
def emit(self, event: AuditEvent) -> None:
|
|
45
|
+
"""Seal ``event`` (stamp ``hashCurrent`` if absent) and POST its canonical
|
|
46
|
+
JSON with ``Authorization: Bearer <token>``. Swallows errors unless
|
|
47
|
+
``swallow_errors`` is False."""
|
|
48
|
+
try:
|
|
49
|
+
if not event.hash_current:
|
|
50
|
+
event = event.model_copy(update={"hash_current": compute_event_hash(event)})
|
|
51
|
+
body = canonical_json(event).encode("utf-8")
|
|
52
|
+
request = urllib.request.Request(
|
|
53
|
+
self._options.endpoint,
|
|
54
|
+
data=body,
|
|
55
|
+
method="POST",
|
|
56
|
+
headers={
|
|
57
|
+
"content-type": "application/json",
|
|
58
|
+
"authorization": f"Bearer {self._options.token}",
|
|
59
|
+
},
|
|
60
|
+
)
|
|
61
|
+
with urllib.request.urlopen(request, timeout=self._options.timeout) as response: # noqa: S310 — endpoint is caller-controlled config, not user input
|
|
62
|
+
if response.status >= 400:
|
|
63
|
+
raise urllib.error.HTTPError(self._options.endpoint, response.status, "audit ingest failed", response.headers, None)
|
|
64
|
+
except Exception as err: # noqa: BLE001 — audit logging must never break the caller
|
|
65
|
+
if not self._options.swallow_errors:
|
|
66
|
+
raise
|
|
67
|
+
if self._options.on_error is not None:
|
|
68
|
+
self._options.on_error(err)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""SHA-256 hash chain over audit events.
|
|
2
|
+
|
|
3
|
+
Each event's ``hash_current`` is ``SHA-256(canonical-JSON(event minus
|
|
4
|
+
hash_current))``; the chain links via ``hash_previous`` = the prior event's
|
|
5
|
+
``hash_current``. Verify by replaying: recompute every hash and confirm each
|
|
6
|
+
event's ``hash_previous`` equals the previous event's ``hash_current``.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import hashlib
|
|
12
|
+
from collections.abc import Mapping
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from .canonical import canonical_json
|
|
16
|
+
from .schema import AuditEvent
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def compute_event_hash(event: AuditEvent | Mapping[str, Any]) -> str:
|
|
20
|
+
"""Return the lowercase hex SHA-256 of ``event`` minus its own
|
|
21
|
+
``hashCurrent`` field. Accepts an :class:`AuditEvent` or a raw wire mapping
|
|
22
|
+
(camelCase keys, as decoded from JSON)."""
|
|
23
|
+
if isinstance(event, AuditEvent):
|
|
24
|
+
payload: Any = event.model_dump(by_alias=True, exclude_unset=True, mode="json")
|
|
25
|
+
else:
|
|
26
|
+
payload = dict(event)
|
|
27
|
+
payload.pop("hashCurrent", None)
|
|
28
|
+
return hashlib.sha256(canonical_json(payload).encode("utf-8")).hexdigest()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def build_hash_chain(events: list[AuditEvent], previous_hash: str | None = None) -> list[AuditEvent]:
|
|
32
|
+
"""Seal an ordered list of events into a chain: stamp each with its
|
|
33
|
+
``hash_previous`` (the prior event's ``hash_current``, or ``previous_hash``
|
|
34
|
+
for the first) and its freshly computed ``hash_current``. Returns new sealed
|
|
35
|
+
copies; inputs are not mutated."""
|
|
36
|
+
sealed: list[AuditEvent] = []
|
|
37
|
+
prev = previous_hash
|
|
38
|
+
for event in events:
|
|
39
|
+
# Omit hash_previous entirely on the first event of a chain (JS undefined,
|
|
40
|
+
# not null) — only stamp it once there is a prior hash to link to.
|
|
41
|
+
update: dict[str, Any] = {"hash_current": None}
|
|
42
|
+
if prev is not None:
|
|
43
|
+
update["hash_previous"] = prev
|
|
44
|
+
with_prev = event.model_copy(update=update)
|
|
45
|
+
current = compute_event_hash(with_prev)
|
|
46
|
+
sealed.append(with_prev.model_copy(update={"hash_current": current}))
|
|
47
|
+
prev = current
|
|
48
|
+
return sealed
|
|
File without changes
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Canonical audit event schema (Python port of ``@smooai/audit``).
|
|
2
|
+
|
|
3
|
+
Mirrors the TypeScript ``AuditEvent`` interface field-for-field, including its
|
|
4
|
+
camelCase wire names. The model uses snake_case attributes with camelCase
|
|
5
|
+
aliases so it is Pythonic to construct yet serializes to the exact keys the
|
|
6
|
+
shared parity corpus (and every other language SDK) expects.
|
|
7
|
+
|
|
8
|
+
The schema is deliberately GENERIC — it carries identity, resource, outcome,
|
|
9
|
+
correlation, request-enrichment, and integrity fields, but ZERO customer
|
|
10
|
+
content beyond the free-form ``metadata`` / ``diff`` bags the emitter fills.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import re
|
|
16
|
+
from typing import Any, Literal
|
|
17
|
+
|
|
18
|
+
from pydantic import BaseModel, ConfigDict
|
|
19
|
+
from pydantic.alias_generators import to_camel
|
|
20
|
+
|
|
21
|
+
AuditActorType = Literal["user", "agent", "system", "integration", "api_client"]
|
|
22
|
+
AuditOutcome = Literal["success", "failure", "denied"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _CamelModel(BaseModel):
|
|
26
|
+
"""Base: snake_case attributes, camelCase aliases, populated by either name."""
|
|
27
|
+
|
|
28
|
+
model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AuditResource(_CamelModel):
|
|
32
|
+
"""Resource an action was performed against (a CRM contact, an agent config…)."""
|
|
33
|
+
|
|
34
|
+
type: str
|
|
35
|
+
id: str
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class AuditDiff(_CamelModel):
|
|
39
|
+
"""Structural diff captured at write time. Either side may be omitted (create
|
|
40
|
+
has no ``before``, delete no ``after``). A PRESENT ``null`` is meaningful and
|
|
41
|
+
is serialized; only an omitted side disappears."""
|
|
42
|
+
|
|
43
|
+
before: Any = None
|
|
44
|
+
after: Any = None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AuditEvent(_CamelModel):
|
|
48
|
+
"""The full audit event shape as persisted — the shared shape every language
|
|
49
|
+
SDK emits and hashes. Field names serialize to camelCase to match the corpus."""
|
|
50
|
+
|
|
51
|
+
id: str
|
|
52
|
+
organization_id: str
|
|
53
|
+
actor_type: AuditActorType
|
|
54
|
+
actor_id: str
|
|
55
|
+
actor_email: str | None = None
|
|
56
|
+
action: str
|
|
57
|
+
resource: AuditResource
|
|
58
|
+
outcome: AuditOutcome
|
|
59
|
+
reason: str | None = None
|
|
60
|
+
session_id: str | None = None
|
|
61
|
+
conversation_id: str | None = None
|
|
62
|
+
ip_address: str | None = None
|
|
63
|
+
user_agent: str | None = None
|
|
64
|
+
geo_country: str | None = None
|
|
65
|
+
diff: AuditDiff | None = None
|
|
66
|
+
metadata: dict[str, Any] = {} # noqa: RUF012 — plain dict default is fine; pydantic deep-copies per instance
|
|
67
|
+
timestamp: str
|
|
68
|
+
hash_previous: str | None = None
|
|
69
|
+
hash_current: str | None = None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# Baseline generic action constants. Emitters are NOT limited to these — any
|
|
73
|
+
# consumer defines its own ``namespace.verb`` actions and emits them directly;
|
|
74
|
+
# the canonical serialization treats ``action`` as an opaque string. The
|
|
75
|
+
# dashboard/alerts pivot off these baseline names, so keep them stable.
|
|
76
|
+
AUDIT_ACTIONS: dict[str, str] = {
|
|
77
|
+
# Identity
|
|
78
|
+
"USER_SIGNIN": "user.signin",
|
|
79
|
+
"USER_SIGNOUT": "user.signout",
|
|
80
|
+
"USER_PASSWORD_CHANGED": "user.password_changed",
|
|
81
|
+
"USER_INVITED": "user.invited",
|
|
82
|
+
# Org
|
|
83
|
+
"ORG_CREATED": "org.created",
|
|
84
|
+
"ORG_MEMBER_ADDED": "org.member_added",
|
|
85
|
+
"ORG_MEMBER_REMOVED": "org.member_removed",
|
|
86
|
+
"ORG_ROLE_CHANGED": "org.role_changed",
|
|
87
|
+
"ORG_SUBSCRIPTION_CHANGED": "org.subscription_changed",
|
|
88
|
+
"ORG_PRODUCT_PURCHASED": "org.product_purchased",
|
|
89
|
+
# Agent
|
|
90
|
+
"AGENT_CONFIG_CHANGED": "agent.config_changed",
|
|
91
|
+
"AGENT_KNOWLEDGE_DOC_ADDED": "agent.knowledge_doc_added",
|
|
92
|
+
"AGENT_KNOWLEDGE_DOC_REMOVED": "agent.knowledge_doc_removed",
|
|
93
|
+
"AGENT_ESCALATION_CREATED": "agent.escalation_created",
|
|
94
|
+
"AGENT_TOOL_FAILED": "agent.tool_failed",
|
|
95
|
+
# CRM
|
|
96
|
+
"CRM_CONTACT_CREATED": "crm.contact_created",
|
|
97
|
+
"CRM_CONTACT_MERGED": "crm.contact_merged",
|
|
98
|
+
"CRM_CONTACT_DELETED": "crm.contact_deleted",
|
|
99
|
+
# API auth
|
|
100
|
+
"API_KEY_MINTED": "api.key_minted",
|
|
101
|
+
"API_KEY_ROTATED": "api.key_rotated",
|
|
102
|
+
"API_KEY_REVOKED": "api.key_revoked",
|
|
103
|
+
# Integrations
|
|
104
|
+
"INTEGRATION_CONNECTED": "integration.connected",
|
|
105
|
+
"INTEGRATION_DISCONNECTED": "integration.disconnected",
|
|
106
|
+
# Google write actions
|
|
107
|
+
"GOOGLE_CALENDAR_EVENT_CREATED": "google.calendar.event_created",
|
|
108
|
+
"GOOGLE_GMAIL_DRAFT_CREATED": "google.gmail.draft_created",
|
|
109
|
+
"GOOGLE_GMAIL_MESSAGE_SENT": "google.gmail.message_sent",
|
|
110
|
+
"GOOGLE_DRIVE_FOLDER_CREATED": "google.drive.folder_created",
|
|
111
|
+
"GOOGLE_DRIVE_FILE_SHARED": "google.drive.file_shared",
|
|
112
|
+
"GOOGLE_BUSINESS_REVIEW_REPLIED": "google.business_profile.review_replied",
|
|
113
|
+
"GOOGLE_BUSINESS_POST_CREATED": "google.business_profile.post_created",
|
|
114
|
+
"GOOGLE_FORMS_FORM_CREATED": "google.forms.form_created",
|
|
115
|
+
"GOOGLE_ADS_CAMPAIGN_PAUSED": "google.ads.campaign_paused",
|
|
116
|
+
"GOOGLE_ADS_CAMPAIGN_ENABLED": "google.ads.campaign_enabled",
|
|
117
|
+
"GOOGLE_BOOKING_CREATED": "google.booking.created",
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
_NAMESPACED_ACTION = re.compile(r"^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]+)+$")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def is_namespaced_action(action: str) -> bool:
|
|
124
|
+
"""Validate the ``namespace.verb`` convention (a lowercase namespace + one or
|
|
125
|
+
more lowercase verb segments, dot-separated). Assert this at your trust
|
|
126
|
+
boundary; canonical serialization treats ``action`` as an opaque string."""
|
|
127
|
+
return _NAMESPACED_ACTION.match(action) is not None
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Parity + behavior tests for the Python audit SDK.
|
|
2
|
+
|
|
3
|
+
The corpus tests are THE gate: every fixture in ``spec/parity-corpus.json`` must
|
|
4
|
+
canonicalize and hash byte-for-byte identically to the TS and Rust SDKs. If any
|
|
5
|
+
fixture fails, the hash chain is broken across stores — fix the serializer, never
|
|
6
|
+
the corpus.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
17
|
+
from smooai_audit import (
|
|
18
|
+
AuditClient,
|
|
19
|
+
AuditClientOptions,
|
|
20
|
+
AuditEvent,
|
|
21
|
+
build_hash_chain,
|
|
22
|
+
canonical_json,
|
|
23
|
+
compute_event_hash,
|
|
24
|
+
is_namespaced_action,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
_CORPUS_PATH = Path(__file__).resolve().parents[2] / "spec" / "parity-corpus.json"
|
|
28
|
+
_FIXTURES: list[dict[str, Any]] = json.loads(_CORPUS_PATH.read_text(encoding="utf-8"))["fixtures"]
|
|
29
|
+
_FIXTURE_IDS = [f["name"] for f in _FIXTURES]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.mark.parametrize("fixture", _FIXTURES, ids=_FIXTURE_IDS)
|
|
33
|
+
def test_corpus_canonical_matches_byte_for_byte(fixture: dict[str, Any]) -> None:
|
|
34
|
+
assert canonical_json(fixture["event"]) == fixture["expectedCanonical"]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@pytest.mark.parametrize("fixture", _FIXTURES, ids=_FIXTURE_IDS)
|
|
38
|
+
def test_corpus_hash_matches(fixture: dict[str, Any]) -> None:
|
|
39
|
+
assert compute_event_hash(fixture["event"]) == fixture["expectedHash"]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.mark.parametrize("fixture", _FIXTURES, ids=_FIXTURE_IDS)
|
|
43
|
+
def test_corpus_via_model_round_trip(fixture: dict[str, Any]) -> None:
|
|
44
|
+
# Parsing the wire dict into the typed model and back must reproduce the same
|
|
45
|
+
# canonical bytes + hash — proves the schema's aliases/optionality are right.
|
|
46
|
+
event = AuditEvent.model_validate(fixture["event"])
|
|
47
|
+
assert canonical_json(event) == fixture["expectedCanonical"]
|
|
48
|
+
assert compute_event_hash(event) == fixture["expectedHash"]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_canonical_sorts_keys_and_preserves_array_order() -> None:
|
|
52
|
+
assert canonical_json({"b": 1, "a": [3, 1, 2]}) == '{"a":[3,1,2],"b":1}'
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_canonical_present_null_is_rendered() -> None:
|
|
56
|
+
assert canonical_json({"a": None}) == '{"a":null}'
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_canonical_forward_slash_not_escaped_control_chars_are() -> None:
|
|
60
|
+
assert canonical_json("a/b\tc") == '"a/b\\tc"'
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_build_hash_chain_links_events() -> None:
|
|
64
|
+
base = dict(
|
|
65
|
+
organization_id="org-1",
|
|
66
|
+
actor_type="user",
|
|
67
|
+
actor_id="u-1",
|
|
68
|
+
action="crm.contact_created",
|
|
69
|
+
resource={"type": "crm.contact", "id": "c-1"},
|
|
70
|
+
outcome="success",
|
|
71
|
+
metadata={},
|
|
72
|
+
timestamp="2026-05-17T12:00:00.000Z",
|
|
73
|
+
)
|
|
74
|
+
events = [AuditEvent(id="e-1", **base), AuditEvent(id="e-2", **base)]
|
|
75
|
+
sealed = build_hash_chain(events)
|
|
76
|
+
|
|
77
|
+
assert sealed[0].hash_previous is None # first event: no link
|
|
78
|
+
assert sealed[0].hash_current is not None
|
|
79
|
+
assert sealed[1].hash_previous == sealed[0].hash_current # chained
|
|
80
|
+
# Recompute independently to prove the stamped hash is correct.
|
|
81
|
+
assert compute_event_hash(sealed[1]) == sealed[1].hash_current
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_is_namespaced_action() -> None:
|
|
85
|
+
assert is_namespaced_action("crm.contact_created")
|
|
86
|
+
assert is_namespaced_action("google.gmail.message_sent")
|
|
87
|
+
assert not is_namespaced_action("nodot")
|
|
88
|
+
assert not is_namespaced_action("Crm.contactCreated")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_emit_swallows_transport_errors_by_default() -> None:
|
|
92
|
+
captured: list[Exception] = []
|
|
93
|
+
client = AuditClient(
|
|
94
|
+
AuditClientOptions(
|
|
95
|
+
endpoint="http://127.0.0.1:1/audit", # nothing listening → connection refused
|
|
96
|
+
token="t",
|
|
97
|
+
timeout=0.5,
|
|
98
|
+
on_error=captured.append,
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
event = AuditEvent(
|
|
102
|
+
id="e-1",
|
|
103
|
+
organization_id="org-1",
|
|
104
|
+
actor_type="user",
|
|
105
|
+
actor_id="u-1",
|
|
106
|
+
action="crm.contact_created",
|
|
107
|
+
resource={"type": "crm.contact", "id": "c-1"},
|
|
108
|
+
outcome="success",
|
|
109
|
+
metadata={},
|
|
110
|
+
timestamp="2026-05-17T12:00:00.000Z",
|
|
111
|
+
)
|
|
112
|
+
client.emit(event) # must not raise
|
|
113
|
+
assert len(captured) == 1
|
|
File without changes
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
revision = 3
|
|
3
|
+
requires-python = ">=3.12"
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "annotated-types"
|
|
7
|
+
version = "0.7.0"
|
|
8
|
+
source = { registry = "https://pypi.org/simple" }
|
|
9
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
|
|
10
|
+
wheels = [
|
|
11
|
+
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "basedpyright"
|
|
16
|
+
version = "1.39.9"
|
|
17
|
+
source = { registry = "https://pypi.org/simple" }
|
|
18
|
+
dependencies = [
|
|
19
|
+
{ name = "nodejs-wheel-binaries" },
|
|
20
|
+
]
|
|
21
|
+
sdist = { url = "https://files.pythonhosted.org/packages/7a/4b/c1f4e211e50389304d6af32b9280e026a7133e3ad59bbdf8f7a3250f8bee/basedpyright-1.39.9.tar.gz", hash = "sha256:32cbea5fc8273e89df3db20daea56cb7286e419ccdfdc479c64759d2dc071901", size = 24412216, upload-time = "2026-06-27T02:19:49.834Z" }
|
|
22
|
+
wheels = [
|
|
23
|
+
{ url = "https://files.pythonhosted.org/packages/2a/d4/e1fa108710d0498a18c77b1e13897f31eab47c69aa8cfe2d2a4df746541e/basedpyright-1.39.9-py3-none-any.whl", hash = "sha256:6b0837b9eba972c71895167ab9b127e6afdbc17abc92312e3f8d15ca82a5611c", size = 13374276, upload-time = "2026-06-27T02:19:54.431Z" },
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[[package]]
|
|
27
|
+
name = "colorama"
|
|
28
|
+
version = "0.4.6"
|
|
29
|
+
source = { registry = "https://pypi.org/simple" }
|
|
30
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
|
31
|
+
wheels = [
|
|
32
|
+
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[[package]]
|
|
36
|
+
name = "iniconfig"
|
|
37
|
+
version = "2.3.0"
|
|
38
|
+
source = { registry = "https://pypi.org/simple" }
|
|
39
|
+
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
|
40
|
+
wheels = [
|
|
41
|
+
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[[package]]
|
|
45
|
+
name = "nodejs-wheel-binaries"
|
|
46
|
+
version = "24.16.0"
|
|
47
|
+
source = { registry = "https://pypi.org/simple" }
|
|
48
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a3/22/2a5beb4e21417c73233d9f65cf6f3e96e891b80d2f550a8f630ebc6b88c6/nodejs_wheel_binaries-24.16.0.tar.gz", hash = "sha256:c973cb69dc5fd16e6f6dc6e579e2c3d5534e2a1f57619dddf5ba070efa7dde37", size = 8056, upload-time = "2026-05-30T16:52:09.807Z" }
|
|
49
|
+
wheels = [
|
|
50
|
+
{ url = "https://files.pythonhosted.org/packages/83/d1/68b43b53cd0fa83ae6fd406705023ca988d9e0ca41c724d82e66fbeb2ef6/nodejs_wheel_binaries-24.16.0-py2.py3-none-macosx_13_0_arm64.whl", hash = "sha256:d9f8f677dcf30e37ac244f07869726abe043f01eb0f45722b1df31cc2af7093c", size = 55666374, upload-time = "2026-05-30T16:51:39.588Z" },
|
|
51
|
+
{ url = "https://files.pythonhosted.org/packages/e9/b2/40a989159599080da485de966c4c2d207e852ac7aa7864702626d96c8bf5/nodejs_wheel_binaries-24.16.0-py2.py3-none-macosx_13_0_x86_64.whl", hash = "sha256:3d0370fe7120ce9697a4f60d40480d2bd8808d9f30131458d5afc0040d4e5a51", size = 55838487, upload-time = "2026-05-30T16:51:43.383Z" },
|
|
52
|
+
{ url = "https://files.pythonhosted.org/packages/d7/a7/cd42174fb5ff6faff7fa8d326a18914d8f232098ab5de055b57c16fa13ca/nodejs_wheel_binaries-24.16.0-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:85dc92bbb79c851569c5925dcc2a4c915a034efab375f99e4e7e6bbe9cca8342", size = 60179540, upload-time = "2026-05-30T16:51:47.036Z" },
|
|
53
|
+
{ url = "https://files.pythonhosted.org/packages/2b/95/c8a1f9ae140aa28df8744d984d01d4b3af7cdd6555af12127f40ceb45a7d/nodejs_wheel_binaries-24.16.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:2f3036292811514ba847b3708492644764f88a833ac425c5f55007014308ddfd", size = 60716262, upload-time = "2026-05-30T16:51:50.711Z" },
|
|
54
|
+
{ url = "https://files.pythonhosted.org/packages/64/c9/7c35b3737f59e36d0249c265397b7bff570519b95301d6e16ea361e904ad/nodejs_wheel_binaries-24.16.0-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:db8a8a76ebd2b28ecbfc9ad464baa3707241b9e050a30e2efdf6f60c0f886502", size = 62230592, upload-time = "2026-05-30T16:51:55Z" },
|
|
55
|
+
{ url = "https://files.pythonhosted.org/packages/04/96/d931255cf9d11a84d6b54d882dba7434646467d568ccf070ea3418638df3/nodejs_wheel_binaries-24.16.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f1a3d8f7b4491cbbd023ba3fc4e901fcca2d9fb80d57f24ba3890de8b1dbac03", size = 62841759, upload-time = "2026-05-30T16:51:59.407Z" },
|
|
56
|
+
{ url = "https://files.pythonhosted.org/packages/a2/7b/8b7a3f41bc255411be30b6d7d288aab8ffd9ea2055db8555ced3548007b9/nodejs_wheel_binaries-24.16.0-py2.py3-none-win_amd64.whl", hash = "sha256:bb136be9944f0662dcf1120f45193a6b75b13fac378971a95cc42c9f879a81aa", size = 42027734, upload-time = "2026-05-30T16:52:03.348Z" },
|
|
57
|
+
{ url = "https://files.pythonhosted.org/packages/17/66/1ed71f1f529b8ca727d42c7ceb9db0bef145ce4a13dfc86fb50aa44f3be6/nodejs_wheel_binaries-24.16.0-py2.py3-none-win_arm64.whl", hash = "sha256:8308940b5edd0a50dc5267ea36ba21c9f668e83fe0d9f293937174d3a7e31c36", size = 39714528, upload-time = "2026-05-30T16:52:06.421Z" },
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[[package]]
|
|
61
|
+
name = "packaging"
|
|
62
|
+
version = "26.2"
|
|
63
|
+
source = { registry = "https://pypi.org/simple" }
|
|
64
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" }
|
|
65
|
+
wheels = [
|
|
66
|
+
{ url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" },
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
[[package]]
|
|
70
|
+
name = "pastel"
|
|
71
|
+
version = "0.2.1"
|
|
72
|
+
source = { registry = "https://pypi.org/simple" }
|
|
73
|
+
sdist = { url = "https://files.pythonhosted.org/packages/76/f1/4594f5e0fcddb6953e5b8fe00da8c317b8b41b547e2b3ae2da7512943c62/pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d", size = 7555, upload-time = "2020-09-16T19:21:12.43Z" }
|
|
74
|
+
wheels = [
|
|
75
|
+
{ url = "https://files.pythonhosted.org/packages/aa/18/a8444036c6dd65ba3624c63b734d3ba95ba63ace513078e1580590075d21/pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364", size = 5955, upload-time = "2020-09-16T19:21:11.409Z" },
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
[[package]]
|
|
79
|
+
name = "pluggy"
|
|
80
|
+
version = "1.6.0"
|
|
81
|
+
source = { registry = "https://pypi.org/simple" }
|
|
82
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
|
83
|
+
wheels = [
|
|
84
|
+
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
[[package]]
|
|
88
|
+
name = "poethepoet"
|
|
89
|
+
version = "0.48.0"
|
|
90
|
+
source = { registry = "https://pypi.org/simple" }
|
|
91
|
+
dependencies = [
|
|
92
|
+
{ name = "pastel" },
|
|
93
|
+
{ name = "pyyaml" },
|
|
94
|
+
]
|
|
95
|
+
sdist = { url = "https://files.pythonhosted.org/packages/5c/92/93a4af9511b8c7c647874521d9e6c904266be98067c2ee1eb2e74520d208/poethepoet-0.48.0.tar.gz", hash = "sha256:a06f49d244fadfc2e2e7faa78b54e64a9694727e4ce1d50e08f23cea3ded74f1", size = 148679, upload-time = "2026-07-05T21:48:30.106Z" }
|
|
96
|
+
wheels = [
|
|
97
|
+
{ url = "https://files.pythonhosted.org/packages/59/8d/d7c9455b15f8d2d7ce57e7b71a8ef8d02d9992ae4283c9777120620c9022/poethepoet-0.48.0-py3-none-any.whl", hash = "sha256:98da6096d060f49b8d84034770265863fb7dc92a40233b7694b9d216ac68737d", size = 185808, upload-time = "2026-07-05T21:48:28.601Z" },
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
[[package]]
|
|
101
|
+
name = "pydantic"
|
|
102
|
+
version = "2.13.4"
|
|
103
|
+
source = { registry = "https://pypi.org/simple" }
|
|
104
|
+
dependencies = [
|
|
105
|
+
{ name = "annotated-types" },
|
|
106
|
+
{ name = "pydantic-core" },
|
|
107
|
+
{ name = "typing-extensions" },
|
|
108
|
+
{ name = "typing-inspection" },
|
|
109
|
+
]
|
|
110
|
+
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" }
|
|
111
|
+
wheels = [
|
|
112
|
+
{ 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" },
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
[[package]]
|
|
116
|
+
name = "pydantic-core"
|
|
117
|
+
version = "2.46.4"
|
|
118
|
+
source = { registry = "https://pypi.org/simple" }
|
|
119
|
+
dependencies = [
|
|
120
|
+
{ name = "typing-extensions" },
|
|
121
|
+
]
|
|
122
|
+
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" }
|
|
123
|
+
wheels = [
|
|
124
|
+
{ url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" },
|
|
125
|
+
{ url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" },
|
|
126
|
+
{ url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" },
|
|
127
|
+
{ url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" },
|
|
128
|
+
{ url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" },
|
|
129
|
+
{ url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" },
|
|
130
|
+
{ url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" },
|
|
131
|
+
{ url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" },
|
|
132
|
+
{ url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" },
|
|
133
|
+
{ url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" },
|
|
134
|
+
{ url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" },
|
|
135
|
+
{ url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" },
|
|
136
|
+
{ url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" },
|
|
137
|
+
{ url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" },
|
|
138
|
+
{ url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" },
|
|
139
|
+
{ url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" },
|
|
140
|
+
{ url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" },
|
|
141
|
+
{ url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" },
|
|
142
|
+
{ url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" },
|
|
143
|
+
{ url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" },
|
|
144
|
+
{ url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" },
|
|
145
|
+
{ url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" },
|
|
146
|
+
{ url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" },
|
|
147
|
+
{ url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" },
|
|
148
|
+
{ url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" },
|
|
149
|
+
{ url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" },
|
|
150
|
+
{ url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" },
|
|
151
|
+
{ url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" },
|
|
152
|
+
{ url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" },
|
|
153
|
+
{ url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" },
|
|
154
|
+
{ 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" },
|
|
155
|
+
{ 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" },
|
|
156
|
+
{ 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" },
|
|
157
|
+
{ 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" },
|
|
158
|
+
{ 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" },
|
|
159
|
+
{ 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" },
|
|
160
|
+
{ 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" },
|
|
161
|
+
{ 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" },
|
|
162
|
+
{ 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" },
|
|
163
|
+
{ 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" },
|
|
164
|
+
{ 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" },
|
|
165
|
+
{ 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" },
|
|
166
|
+
{ 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" },
|
|
167
|
+
{ 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" },
|
|
168
|
+
{ 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" },
|
|
169
|
+
{ 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" },
|
|
170
|
+
{ 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" },
|
|
171
|
+
{ 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" },
|
|
172
|
+
{ 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" },
|
|
173
|
+
{ 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" },
|
|
174
|
+
{ 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" },
|
|
175
|
+
{ 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" },
|
|
176
|
+
{ 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" },
|
|
177
|
+
{ 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" },
|
|
178
|
+
{ 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" },
|
|
179
|
+
{ 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" },
|
|
180
|
+
{ 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" },
|
|
181
|
+
{ 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" },
|
|
182
|
+
{ 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" },
|
|
183
|
+
{ 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" },
|
|
184
|
+
{ url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" },
|
|
185
|
+
{ url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" },
|
|
186
|
+
{ url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" },
|
|
187
|
+
{ url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" },
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
[[package]]
|
|
191
|
+
name = "pygments"
|
|
192
|
+
version = "2.20.0"
|
|
193
|
+
source = { registry = "https://pypi.org/simple" }
|
|
194
|
+
sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
|
|
195
|
+
wheels = [
|
|
196
|
+
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
[[package]]
|
|
200
|
+
name = "pytest"
|
|
201
|
+
version = "9.1.1"
|
|
202
|
+
source = { registry = "https://pypi.org/simple" }
|
|
203
|
+
dependencies = [
|
|
204
|
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
205
|
+
{ name = "iniconfig" },
|
|
206
|
+
{ name = "packaging" },
|
|
207
|
+
{ name = "pluggy" },
|
|
208
|
+
{ name = "pygments" },
|
|
209
|
+
]
|
|
210
|
+
sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
|
|
211
|
+
wheels = [
|
|
212
|
+
{ url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
[[package]]
|
|
216
|
+
name = "pyyaml"
|
|
217
|
+
version = "6.0.3"
|
|
218
|
+
source = { registry = "https://pypi.org/simple" }
|
|
219
|
+
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
|
|
220
|
+
wheels = [
|
|
221
|
+
{ url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
|
|
222
|
+
{ url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
|
|
223
|
+
{ url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
|
|
224
|
+
{ url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
|
|
225
|
+
{ url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
|
|
226
|
+
{ url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
|
|
227
|
+
{ url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
|
|
228
|
+
{ url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
|
|
229
|
+
{ url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
|
|
230
|
+
{ url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
|
|
231
|
+
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
|
|
232
|
+
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
|
|
233
|
+
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
|
|
234
|
+
{ url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
|
|
235
|
+
{ url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
|
|
236
|
+
{ url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
|
|
237
|
+
{ url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
|
|
238
|
+
{ url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
|
|
239
|
+
{ url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
|
|
240
|
+
{ url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
|
|
241
|
+
{ url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
|
|
242
|
+
{ url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
|
|
243
|
+
{ url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
|
|
244
|
+
{ url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
|
|
245
|
+
{ url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
|
|
246
|
+
{ url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
|
|
247
|
+
{ url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
|
|
248
|
+
{ url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
|
|
249
|
+
{ url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
|
|
250
|
+
{ url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
|
|
251
|
+
{ url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
|
|
252
|
+
{ url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
|
|
253
|
+
{ url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
|
|
254
|
+
{ url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
|
|
255
|
+
{ url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
|
|
256
|
+
{ url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
|
|
257
|
+
{ url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
|
|
258
|
+
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
|
|
259
|
+
]
|
|
260
|
+
|
|
261
|
+
[[package]]
|
|
262
|
+
name = "ruff"
|
|
263
|
+
version = "0.11.13"
|
|
264
|
+
source = { registry = "https://pypi.org/simple" }
|
|
265
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ed/da/9c6f995903b4d9474b39da91d2d626659af3ff1eeb43e9ae7c119349dba6/ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514", size = 4282054, upload-time = "2025-06-05T21:00:15.721Z" }
|
|
266
|
+
wheels = [
|
|
267
|
+
{ url = "https://files.pythonhosted.org/packages/7d/ce/a11d381192966e0b4290842cc8d4fac7dc9214ddf627c11c1afff87da29b/ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46", size = 10292516, upload-time = "2025-06-05T20:59:32.944Z" },
|
|
268
|
+
{ url = "https://files.pythonhosted.org/packages/78/db/87c3b59b0d4e753e40b6a3b4a2642dfd1dcaefbff121ddc64d6c8b47ba00/ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48", size = 11106083, upload-time = "2025-06-05T20:59:37.03Z" },
|
|
269
|
+
{ url = "https://files.pythonhosted.org/packages/77/79/d8cec175856ff810a19825d09ce700265f905c643c69f45d2b737e4a470a/ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b", size = 10436024, upload-time = "2025-06-05T20:59:39.741Z" },
|
|
270
|
+
{ url = "https://files.pythonhosted.org/packages/8b/5b/f6d94f2980fa1ee854b41568368a2e1252681b9238ab2895e133d303538f/ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a", size = 10646324, upload-time = "2025-06-05T20:59:42.185Z" },
|
|
271
|
+
{ url = "https://files.pythonhosted.org/packages/6c/9c/b4c2acf24ea4426016d511dfdc787f4ce1ceb835f3c5fbdbcb32b1c63bda/ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc", size = 10174416, upload-time = "2025-06-05T20:59:44.319Z" },
|
|
272
|
+
{ url = "https://files.pythonhosted.org/packages/f3/10/e2e62f77c65ede8cd032c2ca39c41f48feabedb6e282bfd6073d81bb671d/ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629", size = 11724197, upload-time = "2025-06-05T20:59:46.935Z" },
|
|
273
|
+
{ url = "https://files.pythonhosted.org/packages/bb/f0/466fe8469b85c561e081d798c45f8a1d21e0b4a5ef795a1d7f1a9a9ec182/ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933", size = 12511615, upload-time = "2025-06-05T20:59:49.534Z" },
|
|
274
|
+
{ url = "https://files.pythonhosted.org/packages/17/0e/cefe778b46dbd0cbcb03a839946c8f80a06f7968eb298aa4d1a4293f3448/ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165", size = 12117080, upload-time = "2025-06-05T20:59:51.654Z" },
|
|
275
|
+
{ url = "https://files.pythonhosted.org/packages/5d/2c/caaeda564cbe103bed145ea557cb86795b18651b0f6b3ff6a10e84e5a33f/ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71", size = 11326315, upload-time = "2025-06-05T20:59:54.469Z" },
|
|
276
|
+
{ url = "https://files.pythonhosted.org/packages/75/f0/782e7d681d660eda8c536962920c41309e6dd4ebcea9a2714ed5127d44bd/ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9", size = 11555640, upload-time = "2025-06-05T20:59:56.986Z" },
|
|
277
|
+
{ url = "https://files.pythonhosted.org/packages/5d/d4/3d580c616316c7f07fb3c99dbecfe01fbaea7b6fd9a82b801e72e5de742a/ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc", size = 10507364, upload-time = "2025-06-05T20:59:59.154Z" },
|
|
278
|
+
{ url = "https://files.pythonhosted.org/packages/5a/dc/195e6f17d7b3ea6b12dc4f3e9de575db7983db187c378d44606e5d503319/ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7", size = 10141462, upload-time = "2025-06-05T21:00:01.481Z" },
|
|
279
|
+
{ url = "https://files.pythonhosted.org/packages/f4/8e/39a094af6967faa57ecdeacb91bedfb232474ff8c3d20f16a5514e6b3534/ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432", size = 11121028, upload-time = "2025-06-05T21:00:04.06Z" },
|
|
280
|
+
{ url = "https://files.pythonhosted.org/packages/5a/c0/b0b508193b0e8a1654ec683ebab18d309861f8bd64e3a2f9648b80d392cb/ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492", size = 11602992, upload-time = "2025-06-05T21:00:06.249Z" },
|
|
281
|
+
{ url = "https://files.pythonhosted.org/packages/7c/91/263e33ab93ab09ca06ce4f8f8547a858cc198072f873ebc9be7466790bae/ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250", size = 10474944, upload-time = "2025-06-05T21:00:08.459Z" },
|
|
282
|
+
{ url = "https://files.pythonhosted.org/packages/46/f4/7c27734ac2073aae8efb0119cae6931b6fb48017adf048fdf85c19337afc/ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3", size = 11548669, upload-time = "2025-06-05T21:00:11.147Z" },
|
|
283
|
+
{ url = "https://files.pythonhosted.org/packages/ec/bf/b273dd11673fed8a6bd46032c0ea2a04b2ac9bfa9c628756a5856ba113b0/ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b", size = 10683928, upload-time = "2025-06-05T21:00:13.758Z" },
|
|
284
|
+
]
|
|
285
|
+
|
|
286
|
+
[[package]]
|
|
287
|
+
name = "smooai-audit"
|
|
288
|
+
version = "0.1.0"
|
|
289
|
+
source = { editable = "." }
|
|
290
|
+
dependencies = [
|
|
291
|
+
{ name = "pydantic" },
|
|
292
|
+
]
|
|
293
|
+
|
|
294
|
+
[package.dev-dependencies]
|
|
295
|
+
dev = [
|
|
296
|
+
{ name = "basedpyright" },
|
|
297
|
+
{ name = "poethepoet" },
|
|
298
|
+
{ name = "pytest" },
|
|
299
|
+
{ name = "ruff" },
|
|
300
|
+
]
|
|
301
|
+
|
|
302
|
+
[package.metadata]
|
|
303
|
+
requires-dist = [{ name = "pydantic", specifier = ">=2.7.0" }]
|
|
304
|
+
|
|
305
|
+
[package.metadata.requires-dev]
|
|
306
|
+
dev = [
|
|
307
|
+
{ name = "basedpyright", specifier = ">=1.29.2" },
|
|
308
|
+
{ name = "poethepoet", specifier = ">=0.29.0" },
|
|
309
|
+
{ name = "pytest", specifier = ">=8.3.0" },
|
|
310
|
+
{ name = "ruff", specifier = ">=0.11.6,<0.12" },
|
|
311
|
+
]
|
|
312
|
+
|
|
313
|
+
[[package]]
|
|
314
|
+
name = "typing-extensions"
|
|
315
|
+
version = "4.16.0"
|
|
316
|
+
source = { registry = "https://pypi.org/simple" }
|
|
317
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
|
|
318
|
+
wheels = [
|
|
319
|
+
{ url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
|
|
320
|
+
]
|
|
321
|
+
|
|
322
|
+
[[package]]
|
|
323
|
+
name = "typing-inspection"
|
|
324
|
+
version = "0.4.2"
|
|
325
|
+
source = { registry = "https://pypi.org/simple" }
|
|
326
|
+
dependencies = [
|
|
327
|
+
{ name = "typing-extensions" },
|
|
328
|
+
]
|
|
329
|
+
sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
|
|
330
|
+
wheels = [
|
|
331
|
+
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
|
|
332
|
+
]
|