city-sdk 0.9.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.
- city_sdk-0.9.0/.gitignore +72 -0
- city_sdk-0.9.0/PKG-INFO +11 -0
- city_sdk-0.9.0/pyproject.toml +23 -0
- city_sdk-0.9.0/src/cap/__init__.py +1 -0
- city_sdk-0.9.0/src/cap/core/__init__.py +32 -0
- city_sdk-0.9.0/src/cap/core/envelope.py +156 -0
- city_sdk-0.9.0/src/cap/core/errors.py +59 -0
- city_sdk-0.9.0/src/cap/core/generated/__init__.py +1 -0
- city_sdk-0.9.0/src/cap/core/generated/clients/agent.py +163 -0
- city_sdk-0.9.0/src/cap/core/generated/clients/attestation.py +104 -0
- city_sdk-0.9.0/src/cap/core/generated/clients/deploy.py +107 -0
- city_sdk-0.9.0/src/cap/core/generated/clients/governance/__init__.py +47 -0
- city_sdk-0.9.0/src/cap/core/generated/clients/governance/city/__init__.py +3 -0
- city_sdk-0.9.0/src/cap/core/generated/clients/governance/city/order.py +22 -0
- city_sdk-0.9.0/src/cap/core/generated/clients/index.py +95 -0
- city_sdk-0.9.0/src/cap/core/generated/clients/ledger.py +22 -0
- city_sdk-0.9.0/src/cap/core/generated/clients/market.py +101 -0
- city_sdk-0.9.0/src/cap/core/generated/clients/relay.py +23 -0
- city_sdk-0.9.0/src/cap/core/generated/events.py +46 -0
- city_sdk-0.9.0/src/cap/core/generated/models/__init__.py +3 -0
- city_sdk-0.9.0/src/cap/core/generated/models/ack.py +23 -0
- city_sdk-0.9.0/src/cap/core/generated/models/cap_probe.py +13 -0
- city_sdk-0.9.0/src/cap/core/generated/models/cap_probe_result.py +13 -0
- city_sdk-0.9.0/src/cap/core/generated/models/card.py +49 -0
- city_sdk-0.9.0/src/cap/core/generated/models/city_order_ack.py +22 -0
- city_sdk-0.9.0/src/cap/core/generated/models/envelope.py +41 -0
- city_sdk-0.9.0/src/cap/core/generated/models/error.py +16 -0
- city_sdk-0.9.0/src/cap/core/generated/models/error_codes.py +27 -0
- city_sdk-0.9.0/src/cap/core/generated/models/grant.py +26 -0
- city_sdk-0.9.0/src/cap/core/generated/models/order.py +20 -0
- city_sdk-0.9.0/src/cap/core/generated/models/order_kinds.py +19 -0
- city_sdk-0.9.0/src/cap/core/grant.py +83 -0
- city_sdk-0.9.0/src/cap/core/http_ports.py +95 -0
- city_sdk-0.9.0/src/cap/core/jcs.py +38 -0
- city_sdk-0.9.0/src/cap/core/jws.py +62 -0
- city_sdk-0.9.0/src/cap/core/keys.py +97 -0
- city_sdk-0.9.0/src/cap/core/process.py +35 -0
- city_sdk-0.9.0/src/cap/core/py.typed +0 -0
- city_sdk-0.9.0/src/cap/core/timeutil.py +28 -0
- city_sdk-0.9.0/src/city_sdk/__init__.py +3 -0
- city_sdk-0.9.0/src/city_sdk/__main__.py +31 -0
- city_sdk-0.9.0/src/city_sdk/adapters.py +17 -0
- city_sdk-0.9.0/src/city_sdk/app.py +51 -0
- city_sdk-0.9.0/src/city_sdk/boot.py +268 -0
- city_sdk-0.9.0/src/city_sdk/cli.py +57 -0
- city_sdk-0.9.0/src/city_sdk/harness.py +379 -0
- city_sdk-0.9.0/src/city_sdk/http.py +223 -0
- city_sdk-0.9.0/src/city_sdk/journal.py +59 -0
- city_sdk-0.9.0/src/city_sdk/layers.py +21 -0
- city_sdk-0.9.0/src/city_sdk/llm.py +74 -0
- city_sdk-0.9.0/src/city_sdk/logs.py +55 -0
- city_sdk-0.9.0/src/city_sdk/market.py +150 -0
- city_sdk-0.9.0/src/city_sdk/policy.py +23 -0
- city_sdk-0.9.0/src/city_sdk/ports.py +126 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Terraform
|
|
2
|
+
**/.terraform/
|
|
3
|
+
*.tfstate
|
|
4
|
+
*.tfstate.backup
|
|
5
|
+
*.tfstate.lock.info
|
|
6
|
+
crash.log
|
|
7
|
+
crash.*.log
|
|
8
|
+
override.tf
|
|
9
|
+
override.tf.json
|
|
10
|
+
*_override.tf
|
|
11
|
+
*_override.tf.json
|
|
12
|
+
*.tfplan
|
|
13
|
+
*.tfplan.json
|
|
14
|
+
*.pyc
|
|
15
|
+
__pycache__/
|
|
16
|
+
|
|
17
|
+
# Local city CLI keystore and team token
|
|
18
|
+
.city/
|
|
19
|
+
|
|
20
|
+
# Terraform variable files with secrets — keep *.tfvars.example, ignore actual values
|
|
21
|
+
*.tfvars
|
|
22
|
+
*.tfvars.json
|
|
23
|
+
|
|
24
|
+
# Terragrunt cache
|
|
25
|
+
**/.terragrunt-cache/
|
|
26
|
+
|
|
27
|
+
# Provider lock files (optional: remove if you want to pin providers in VCS)
|
|
28
|
+
# **/.terraform.lock.hcl
|
|
29
|
+
|
|
30
|
+
# GCP service account key files
|
|
31
|
+
*.json
|
|
32
|
+
!**/cloudbuild*.json
|
|
33
|
+
# Firebase web app configs are public client identifiers (not secrets)
|
|
34
|
+
!**/firebase-config*.json
|
|
35
|
+
# Contract schemas and examples are source, not credentials
|
|
36
|
+
!specs/**/*.json
|
|
37
|
+
|
|
38
|
+
# macOS
|
|
39
|
+
.DS_Store
|
|
40
|
+
.AppleDouble
|
|
41
|
+
.LSOverride
|
|
42
|
+
|
|
43
|
+
# Editor
|
|
44
|
+
.idea/
|
|
45
|
+
.vscode/
|
|
46
|
+
*.swp
|
|
47
|
+
*.swo
|
|
48
|
+
*~
|
|
49
|
+
|
|
50
|
+
# Node.js / Next.js config files
|
|
51
|
+
!package-lock.json
|
|
52
|
+
!package.json
|
|
53
|
+
!tsconfig.json
|
|
54
|
+
!next.config.mjs
|
|
55
|
+
!next.config.js
|
|
56
|
+
node_modules/
|
|
57
|
+
services/ui/dist/
|
|
58
|
+
services/ui/.env
|
|
59
|
+
services/ui/.env.local
|
|
60
|
+
services/ui/.env.*.local
|
|
61
|
+
.venv/
|
|
62
|
+
.mypy_cache/
|
|
63
|
+
.ruff_cache/
|
|
64
|
+
.pytest_cache/
|
|
65
|
+
|
|
66
|
+
# Elementor kit workdir / local backups (keep the published zip + README tracked)
|
|
67
|
+
landing/elementor-export/.kit-work/
|
|
68
|
+
landing/elementor-export/*.bak.zip
|
|
69
|
+
landing/elementor-export/migrate_to_native.py
|
|
70
|
+
landing/elementor-export/pni-elementor-kit.zip
|
|
71
|
+
landing/elementor-export/README.md
|
|
72
|
+
.publish/
|
city_sdk-0.9.0/PKG-INFO
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: city-sdk
|
|
3
|
+
Version: 0.9.0
|
|
4
|
+
Summary: City harness and the day-1 Market facade (includes cap).
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Requires-Dist: cryptography>=43
|
|
7
|
+
Requires-Dist: fastapi>=0.115
|
|
8
|
+
Requires-Dist: httpx>=0.27
|
|
9
|
+
Requires-Dist: pydantic>=2
|
|
10
|
+
Requires-Dist: rfc8785>=0.1.4
|
|
11
|
+
Requires-Dist: uvicorn>=0.32
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "city-sdk"
|
|
3
|
+
version = "0.9.0"
|
|
4
|
+
description = "City harness and the day-1 Market facade (includes cap)."
|
|
5
|
+
requires-python = ">=3.12"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"cryptography>=43",
|
|
8
|
+
"fastapi>=0.115",
|
|
9
|
+
"httpx>=0.27",
|
|
10
|
+
"pydantic>=2",
|
|
11
|
+
"rfc8785>=0.1.4",
|
|
12
|
+
"uvicorn>=0.32",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
city-sdk = "city_sdk.__main__:main"
|
|
17
|
+
|
|
18
|
+
[build-system]
|
|
19
|
+
requires = ["hatchling"]
|
|
20
|
+
build-backend = "hatchling.build"
|
|
21
|
+
|
|
22
|
+
[tool.hatch.build.targets.wheel]
|
|
23
|
+
packages = ["src/city_sdk", "src/cap"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CAP library namespace (``cap.core``, later ``cap.sdk``, …)."""
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""CAP canonicalization, signatures, grants, envelopes, and error codes."""
|
|
2
|
+
|
|
3
|
+
from cap.core.envelope import build, sign, verify
|
|
4
|
+
from cap.core.errors import CapError, http_status_for
|
|
5
|
+
from cap.core.grant import SignedGrant
|
|
6
|
+
from cap.core.grant import issue as issue_grant
|
|
7
|
+
from cap.core.grant import verify as verify_grant
|
|
8
|
+
from cap.core.jcs import canonicalize, card_digest, digest
|
|
9
|
+
from cap.core.jws import sign_detached, verify_detached
|
|
10
|
+
from cap.core.keys import KeyPair, PrivateKey, PublicKey, generate
|
|
11
|
+
|
|
12
|
+
__version__ = "0.9.0"
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"CapError",
|
|
16
|
+
"KeyPair",
|
|
17
|
+
"PrivateKey",
|
|
18
|
+
"PublicKey",
|
|
19
|
+
"SignedGrant",
|
|
20
|
+
"build",
|
|
21
|
+
"canonicalize",
|
|
22
|
+
"card_digest",
|
|
23
|
+
"digest",
|
|
24
|
+
"generate",
|
|
25
|
+
"http_status_for",
|
|
26
|
+
"issue_grant",
|
|
27
|
+
"sign",
|
|
28
|
+
"sign_detached",
|
|
29
|
+
"verify",
|
|
30
|
+
"verify_detached",
|
|
31
|
+
"verify_grant",
|
|
32
|
+
]
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""Build, sign, and verify a CAP envelope."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable, Mapping
|
|
6
|
+
from datetime import datetime, timedelta
|
|
7
|
+
|
|
8
|
+
from cap.core.errors import CapError
|
|
9
|
+
from cap.core.grant import SignedGrant
|
|
10
|
+
from cap.core.grant import verify as verify_grant
|
|
11
|
+
from cap.core.jcs import canonicalize
|
|
12
|
+
from cap.core.jws import sign_detached, verify_detached
|
|
13
|
+
from cap.core.keys import PrivateKey, PublicKey
|
|
14
|
+
from cap.core.timeutil import format_time, parse_time
|
|
15
|
+
|
|
16
|
+
_SKEW = timedelta(seconds=120)
|
|
17
|
+
_DEFAULT_TTL = timedelta(seconds=60)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def build(
|
|
21
|
+
*,
|
|
22
|
+
msg_id: str,
|
|
23
|
+
message_type: str,
|
|
24
|
+
sender: str,
|
|
25
|
+
recipient: str,
|
|
26
|
+
sent_at: datetime,
|
|
27
|
+
body: Mapping[str, object],
|
|
28
|
+
profile: str | None = None,
|
|
29
|
+
expires_at: datetime | None = None,
|
|
30
|
+
conversation_id: str | None = None,
|
|
31
|
+
need_id: str | None = None,
|
|
32
|
+
in_reply_to: str | None = None,
|
|
33
|
+
traceparent: str | None = None,
|
|
34
|
+
) -> dict[str, object]:
|
|
35
|
+
envelope: dict[str, object] = {
|
|
36
|
+
"cap": "1.0",
|
|
37
|
+
"msg_id": msg_id,
|
|
38
|
+
"type": message_type,
|
|
39
|
+
"from": sender,
|
|
40
|
+
"to": recipient,
|
|
41
|
+
"sent_at": format_time(sent_at),
|
|
42
|
+
"body": dict(body),
|
|
43
|
+
}
|
|
44
|
+
if profile is not None:
|
|
45
|
+
envelope["profile"] = profile
|
|
46
|
+
if expires_at is not None:
|
|
47
|
+
envelope["expires_at"] = format_time(expires_at)
|
|
48
|
+
if conversation_id is not None:
|
|
49
|
+
envelope["conversation_id"] = conversation_id
|
|
50
|
+
if need_id is not None:
|
|
51
|
+
envelope["need_id"] = need_id
|
|
52
|
+
if in_reply_to is not None:
|
|
53
|
+
envelope["in_reply_to"] = in_reply_to
|
|
54
|
+
if traceparent is not None:
|
|
55
|
+
envelope["traceparent"] = traceparent
|
|
56
|
+
return envelope
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def sign(
|
|
60
|
+
envelope: Mapping[str, object],
|
|
61
|
+
private: PrivateKey,
|
|
62
|
+
*,
|
|
63
|
+
kid: str,
|
|
64
|
+
grant_id: str | None = None,
|
|
65
|
+
) -> dict[str, object]:
|
|
66
|
+
unsigned = {key: value for key, value in envelope.items() if key != "sig"}
|
|
67
|
+
sender = unsigned.get("from")
|
|
68
|
+
if not isinstance(sender, str) or not kid.startswith(sender + "#"):
|
|
69
|
+
raise CapError("KEY_UNKNOWN", "kid is not under from")
|
|
70
|
+
sig: dict[str, str] = {"alg": "EdDSA", "kid": kid}
|
|
71
|
+
if grant_id is not None:
|
|
72
|
+
sig["grant"] = grant_id
|
|
73
|
+
sig["jws"] = sign_detached(private, canonicalize(unsigned).encode("utf-8"), kid=kid)
|
|
74
|
+
signed = dict(unsigned)
|
|
75
|
+
signed["sig"] = sig
|
|
76
|
+
return signed
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def verify(
|
|
80
|
+
envelope: Mapping[str, object],
|
|
81
|
+
*,
|
|
82
|
+
now: datetime,
|
|
83
|
+
key_for_kid: Callable[[str], PublicKey],
|
|
84
|
+
grant_for_id: Callable[[str], SignedGrant] | None = None,
|
|
85
|
+
controller_public: PublicKey | None = None,
|
|
86
|
+
required_permission: str | None = None,
|
|
87
|
+
target_visible: Callable[[str], bool] | None = None,
|
|
88
|
+
record_verifier: Callable[[Mapping[str, object]], None] | None = None,
|
|
89
|
+
) -> None:
|
|
90
|
+
"""Verify signature, ``from``/``kid``, grant chain, expiry, and clock skew.
|
|
91
|
+
|
|
92
|
+
``target_visible`` defaults to true. The Relay supplies the Index check.
|
|
93
|
+
"""
|
|
94
|
+
if now.tzinfo is None:
|
|
95
|
+
raise CapError("SCHEMA_INVALID", "now must be timezone-aware")
|
|
96
|
+
msg_id = envelope.get("msg_id")
|
|
97
|
+
msg = msg_id if isinstance(msg_id, str) else None
|
|
98
|
+
unsigned = {key: value for key, value in envelope.items() if key != "sig"}
|
|
99
|
+
for field in ("cap", "msg_id", "type", "from", "to", "sent_at", "body"):
|
|
100
|
+
if field not in unsigned:
|
|
101
|
+
raise CapError("SCHEMA_INVALID", f"missing {field}", msg)
|
|
102
|
+
if unsigned.get("cap") != "1.0":
|
|
103
|
+
raise CapError("SCHEMA_INVALID", "cap must be 1.0", msg)
|
|
104
|
+
sender = unsigned["from"]
|
|
105
|
+
recipient = unsigned["to"]
|
|
106
|
+
if not isinstance(sender, str) or not isinstance(recipient, str):
|
|
107
|
+
raise CapError("SCHEMA_INVALID", "from and to must be strings", msg)
|
|
108
|
+
sig = envelope.get("sig")
|
|
109
|
+
if not isinstance(sig, Mapping):
|
|
110
|
+
raise CapError("SCHEMA_INVALID", "sig is required", msg)
|
|
111
|
+
if sig.get("alg") != "EdDSA":
|
|
112
|
+
raise CapError("SIGNATURE_INVALID", "sig.alg must be EdDSA", msg)
|
|
113
|
+
kid = sig.get("kid")
|
|
114
|
+
token = sig.get("jws")
|
|
115
|
+
if not isinstance(kid, str) or not isinstance(token, str):
|
|
116
|
+
raise CapError("SCHEMA_INVALID", "sig.kid and sig.jws are required", msg)
|
|
117
|
+
if not kid.startswith(sender + "#"):
|
|
118
|
+
raise CapError("KEY_UNKNOWN", "kid is not under from", msg)
|
|
119
|
+
try:
|
|
120
|
+
public = key_for_kid(kid)
|
|
121
|
+
except KeyError as exc:
|
|
122
|
+
raise CapError("KEY_UNKNOWN", kid, msg) from exc
|
|
123
|
+
verify_detached(public, canonicalize(unsigned).encode("utf-8"), token, kid=kid)
|
|
124
|
+
grant_id = sig.get("grant")
|
|
125
|
+
if grant_id is not None:
|
|
126
|
+
if not isinstance(grant_id, str):
|
|
127
|
+
raise CapError("SCHEMA_INVALID", "sig.grant must be a string", msg)
|
|
128
|
+
if grant_for_id is None or controller_public is None:
|
|
129
|
+
raise CapError("SIGNATURE_INVALID", "grant resolver is required", msg)
|
|
130
|
+
try:
|
|
131
|
+
signed_grant = grant_for_id(grant_id)
|
|
132
|
+
except KeyError as exc:
|
|
133
|
+
raise CapError("SIGNATURE_INVALID", "unknown grant", msg) from exc
|
|
134
|
+
if signed_grant.grant.get("grant_id") != grant_id:
|
|
135
|
+
raise CapError("SIGNATURE_INVALID", "grant_id mismatch", msg)
|
|
136
|
+
if signed_grant.grant.get("agent_id") != sender:
|
|
137
|
+
raise CapError("SIGNATURE_INVALID", "grant agent_id mismatch", msg)
|
|
138
|
+
if signed_grant.grant.get("agent_pubkey") != public.to_jwk():
|
|
139
|
+
raise CapError("KEY_UNKNOWN", "grant key does not match kid", msg)
|
|
140
|
+
verify_grant(
|
|
141
|
+
signed_grant,
|
|
142
|
+
controller_public,
|
|
143
|
+
now=now,
|
|
144
|
+
required_permission=required_permission,
|
|
145
|
+
record_verifier=record_verifier,
|
|
146
|
+
)
|
|
147
|
+
sent_at = parse_time(unsigned["sent_at"])
|
|
148
|
+
if abs(now - sent_at) > _SKEW:
|
|
149
|
+
raise CapError("EXPIRED", "sent_at outside ±120s", msg)
|
|
150
|
+
expires_raw = unsigned.get("expires_at")
|
|
151
|
+
expires_at = parse_time(expires_raw) if expires_raw is not None else sent_at + _DEFAULT_TTL
|
|
152
|
+
if now > expires_at:
|
|
153
|
+
raise CapError("EXPIRED", "expires_at passed", msg)
|
|
154
|
+
visible = True if target_visible is None else target_visible(recipient)
|
|
155
|
+
if not visible:
|
|
156
|
+
raise CapError("TARGET_NOT_VISIBLE", recipient, msg)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""City error codes and the CAP error body (``specs/cap/error.json``)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
# specification 04 §6.9. Profile codes are listed there and are not in error-codes.json.
|
|
6
|
+
HTTP_STATUS: dict[str, int] = {
|
|
7
|
+
"SCHEMA_INVALID": 400,
|
|
8
|
+
"SIGNATURE_INVALID": 401,
|
|
9
|
+
"KEY_UNKNOWN": 401,
|
|
10
|
+
"GRANT_EXPIRED": 401,
|
|
11
|
+
"GRANT_PERMISSION_DENIED": 403,
|
|
12
|
+
"TARGET_NOT_VISIBLE": 404,
|
|
13
|
+
"IDEMPOTENCY_CONFLICT": 400,
|
|
14
|
+
"DUPLICATE": 409,
|
|
15
|
+
"EXPIRED": 410,
|
|
16
|
+
"POLICY_VIOLATION": 422,
|
|
17
|
+
"STALE_FENCE": 422,
|
|
18
|
+
"STALE_EPOCH": 422,
|
|
19
|
+
"OUT_OF_SCOPE": 422,
|
|
20
|
+
"NO_CAPACITY": 422,
|
|
21
|
+
"CONSENT_MISSING": 422,
|
|
22
|
+
"RATE_LIMITED": 429,
|
|
23
|
+
"DRAINING": 503,
|
|
24
|
+
"QUARANTINED": 503,
|
|
25
|
+
"SUSPENDED": 503,
|
|
26
|
+
"UPSTREAM_TIMEOUT": 504,
|
|
27
|
+
"AGENT_LOST_CUSTODY": 502,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class CapError(Exception):
|
|
32
|
+
"""A CAP failure. ``body()`` matches ``specs/cap/error.json``."""
|
|
33
|
+
|
|
34
|
+
def __init__(self, code: str, message: str = "", msg_id: str | None = None) -> None:
|
|
35
|
+
self.code = code
|
|
36
|
+
self.message = message
|
|
37
|
+
self.msg_id = msg_id
|
|
38
|
+
super().__init__(code if not message else f"{code}: {message}")
|
|
39
|
+
|
|
40
|
+
def http_status(self) -> int:
|
|
41
|
+
try:
|
|
42
|
+
return HTTP_STATUS[self.code]
|
|
43
|
+
except KeyError as exc:
|
|
44
|
+
raise CapError("SCHEMA_INVALID", f"unknown error code {self.code}") from exc
|
|
45
|
+
|
|
46
|
+
def body(self) -> dict[str, str]:
|
|
47
|
+
payload: dict[str, str] = {"code": self.code}
|
|
48
|
+
if self.message:
|
|
49
|
+
payload["message"] = self.message
|
|
50
|
+
if self.msg_id is not None:
|
|
51
|
+
payload["msg_id"] = self.msg_id
|
|
52
|
+
return payload
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def http_status_for(code: str) -> int:
|
|
56
|
+
try:
|
|
57
|
+
return HTTP_STATUS[code]
|
|
58
|
+
except KeyError as exc:
|
|
59
|
+
raise CapError("SCHEMA_INVALID", f"unknown error code {code}") from exc
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Models produced by ``make gen``. Do not edit by hand."""
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# generated by datamodel-codegen:
|
|
2
|
+
# filename: agent.yaml
|
|
3
|
+
# timestamp: 2026-10-10T14:29:54+00:00
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
from enum import Enum, StrEnum
|
|
7
|
+
from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel, conint, constr
|
|
8
|
+
from typing import Any, Literal
|
|
9
|
+
from uuid import UUID
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Status(Enum):
|
|
13
|
+
ok = "ok"
|
|
14
|
+
degraded = "degraded"
|
|
15
|
+
draining = "draining"
|
|
16
|
+
quarantined = "quarantined"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Policy(BaseModel):
|
|
20
|
+
epoch: int
|
|
21
|
+
digest: str
|
|
22
|
+
applied_at: AwareDatetime | None = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Health(BaseModel):
|
|
26
|
+
model_config = ConfigDict(
|
|
27
|
+
extra="forbid",
|
|
28
|
+
)
|
|
29
|
+
status: Status
|
|
30
|
+
agent_id: str
|
|
31
|
+
version: str
|
|
32
|
+
cap_versions: list[str]
|
|
33
|
+
profiles: list[str]
|
|
34
|
+
card_digest: str
|
|
35
|
+
policy: Policy | None = None
|
|
36
|
+
load: dict[str, Any] | None = None
|
|
37
|
+
build: dict[str, Any] | None = None
|
|
38
|
+
models: list[dict[str, Any]] | None = None
|
|
39
|
+
uptime_s: conint(ge=0)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Delivery1(BaseModel):
|
|
43
|
+
model_config = ConfigDict(
|
|
44
|
+
extra="forbid",
|
|
45
|
+
)
|
|
46
|
+
msg_id: UUID
|
|
47
|
+
mailbox: str
|
|
48
|
+
fence: conint(ge=1)
|
|
49
|
+
attempt: conint(ge=1)
|
|
50
|
+
lease_until: AwareDatetime
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class Sig(BaseModel):
|
|
54
|
+
model_config = ConfigDict(
|
|
55
|
+
extra="forbid",
|
|
56
|
+
)
|
|
57
|
+
alg: Literal["EdDSA"]
|
|
58
|
+
kid: str
|
|
59
|
+
grant: str | None = None
|
|
60
|
+
jws: str
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class Envelope(BaseModel):
|
|
64
|
+
model_config = ConfigDict(
|
|
65
|
+
extra="forbid",
|
|
66
|
+
)
|
|
67
|
+
cap: Literal["1.0"]
|
|
68
|
+
profile: constr(pattern=r"^[a-z][a-z0-9-]*/[0-9]+\.[0-9]+$") | None = None
|
|
69
|
+
msg_id: UUID
|
|
70
|
+
type: constr(pattern=r"^[a-z][a-z0-9.-]*$")
|
|
71
|
+
from_: constr(pattern=r"^[a-z][a-z0-9-]*:[A-Za-z0-9][A-Za-z0-9._/-]*$") = Field(
|
|
72
|
+
..., alias="from"
|
|
73
|
+
)
|
|
74
|
+
to: constr(pattern=r"^[a-z][a-z0-9-]*:[A-Za-z0-9][A-Za-z0-9._/-]*$")
|
|
75
|
+
conversation_id: constr(max_length=128) | None = None
|
|
76
|
+
need_id: constr(max_length=128) | None = None
|
|
77
|
+
in_reply_to: UUID | None = None
|
|
78
|
+
fence: conint(ge=1) | None = None
|
|
79
|
+
sent_at: AwareDatetime
|
|
80
|
+
expires_at: AwareDatetime | None = None
|
|
81
|
+
traceparent: constr(pattern=r"^00-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$") | None = None
|
|
82
|
+
body: dict[str, Any]
|
|
83
|
+
sig: Sig
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class Status1(Enum):
|
|
87
|
+
processed = "processed"
|
|
88
|
+
received = "received"
|
|
89
|
+
rejected = "rejected"
|
|
90
|
+
retryable = "retryable"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class DeliveryAck(BaseModel):
|
|
94
|
+
status: Status1
|
|
95
|
+
envelopes: list[Envelope] | None = None
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class Probe(StrEnum):
|
|
99
|
+
live = "live"
|
|
100
|
+
ready = "ready"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class Error(BaseModel):
|
|
104
|
+
model_config = ConfigDict(
|
|
105
|
+
extra="forbid",
|
|
106
|
+
)
|
|
107
|
+
code: constr(pattern=r"^[A-Z][A-Z0-9_]*$")
|
|
108
|
+
message: str | None = None
|
|
109
|
+
msg_id: UUID | None = None
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class Profile(RootModel[constr(pattern=r"^[a-z][a-z0-9-]*/[0-9]+\.[0-9]+$")]):
|
|
113
|
+
root: constr(pattern=r"^[a-z][a-z0-9-]*/[0-9]+\.[0-9]+$")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class Endpoints(BaseModel):
|
|
117
|
+
model_config = ConfigDict(
|
|
118
|
+
extra="forbid",
|
|
119
|
+
)
|
|
120
|
+
mailbox: str
|
|
121
|
+
send_via: str | None = None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class Key(BaseModel):
|
|
125
|
+
model_config = ConfigDict(
|
|
126
|
+
extra="forbid",
|
|
127
|
+
)
|
|
128
|
+
kid: str
|
|
129
|
+
kty: Literal["OKP"]
|
|
130
|
+
crv: Literal["Ed25519"]
|
|
131
|
+
x: str
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class Card(BaseModel):
|
|
135
|
+
agent_id: constr(pattern=r"^agent:[a-z0-9-]+/[a-z0-9-]+$")
|
|
136
|
+
team: constr(pattern=r"^team:[a-z0-9-]+$")
|
|
137
|
+
name: str
|
|
138
|
+
version: constr(pattern=r"^[0-9]+\.[0-9]+\.[0-9]+$")
|
|
139
|
+
cap_versions: list[Literal["1.0"]] = Field(..., min_length=1)
|
|
140
|
+
profiles: list[Profile] = Field(..., min_length=1)
|
|
141
|
+
data_processing: list[dict[str, Any]] | None = None
|
|
142
|
+
ai_act_transparency: str | None = None
|
|
143
|
+
governable: list[str] | None = None
|
|
144
|
+
max_inflight: conint(ge=1) | None = None
|
|
145
|
+
consent: dict[str, Any] | None = None
|
|
146
|
+
build: dict[str, Any] | None = None
|
|
147
|
+
dependencies: dict[str, Any] | None = None
|
|
148
|
+
endpoints: Endpoints
|
|
149
|
+
keys: list[Key] = Field(..., min_length=1)
|
|
150
|
+
grant: str | None = None
|
|
151
|
+
signatures: dict[str, Any] | None = None
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class AuditGetResponse(BaseModel):
|
|
155
|
+
entries: list[dict[str, Any]]
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class Delivery(BaseModel):
|
|
159
|
+
model_config = ConfigDict(
|
|
160
|
+
extra="forbid",
|
|
161
|
+
)
|
|
162
|
+
delivery: Delivery1
|
|
163
|
+
envelope: Envelope
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# generated by datamodel-codegen:
|
|
2
|
+
# filename: attestation.yaml
|
|
3
|
+
# timestamp: 2026-10-10T14:29:54+00:00
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
from typing import Any, Literal
|
|
7
|
+
from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel, conint, constr
|
|
8
|
+
from uuid import UUID
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CityEvent1(BaseModel):
|
|
12
|
+
model_config = ConfigDict(
|
|
13
|
+
extra="allow",
|
|
14
|
+
)
|
|
15
|
+
record_kind: Literal["intent"]
|
|
16
|
+
record_id: str
|
|
17
|
+
observer: str
|
|
18
|
+
actor: str
|
|
19
|
+
subject: str | None = None
|
|
20
|
+
action: str
|
|
21
|
+
msg_type: str | None = None
|
|
22
|
+
msg_id: str | None = None
|
|
23
|
+
conversation_id: str | None = None
|
|
24
|
+
need_id: str | None = None
|
|
25
|
+
trace_id: str | None = None
|
|
26
|
+
time: AwareDatetime
|
|
27
|
+
envelope_digest: str | None = None
|
|
28
|
+
envelope_sig: str | None = None
|
|
29
|
+
payload_ref: str | None = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CityEvent2(BaseModel):
|
|
33
|
+
model_config = ConfigDict(
|
|
34
|
+
extra="allow",
|
|
35
|
+
)
|
|
36
|
+
record_kind: Literal["event"]
|
|
37
|
+
record_id: str
|
|
38
|
+
observer: str
|
|
39
|
+
type: str
|
|
40
|
+
about_record: str | None = None
|
|
41
|
+
outcome: dict[str, Any] | None = None
|
|
42
|
+
transport_policy: str | None = None
|
|
43
|
+
time: AwareDatetime
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class CityEvent(RootModel[CityEvent1 | CityEvent2]):
|
|
47
|
+
root: CityEvent1 | CityEvent2 = Field(..., title="city-events record")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Sig(BaseModel):
|
|
51
|
+
model_config = ConfigDict(
|
|
52
|
+
extra="forbid",
|
|
53
|
+
)
|
|
54
|
+
alg: Literal["EdDSA"]
|
|
55
|
+
kid: str
|
|
56
|
+
grant: str | None = None
|
|
57
|
+
jws: str
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class Envelope(BaseModel):
|
|
61
|
+
model_config = ConfigDict(
|
|
62
|
+
extra="forbid",
|
|
63
|
+
)
|
|
64
|
+
cap: Literal["1.0"]
|
|
65
|
+
profile: constr(pattern=r"^[a-z][a-z0-9-]*/[0-9]+\.[0-9]+$") | None = None
|
|
66
|
+
msg_id: UUID
|
|
67
|
+
type: constr(pattern=r"^[a-z][a-z0-9.-]*$")
|
|
68
|
+
from_: constr(pattern=r"^[a-z][a-z0-9-]*:[A-Za-z0-9][A-Za-z0-9._/-]*$") = Field(
|
|
69
|
+
..., alias="from"
|
|
70
|
+
)
|
|
71
|
+
to: constr(pattern=r"^[a-z][a-z0-9-]*:[A-Za-z0-9][A-Za-z0-9._/-]*$")
|
|
72
|
+
conversation_id: constr(max_length=128) | None = None
|
|
73
|
+
need_id: constr(max_length=128) | None = None
|
|
74
|
+
in_reply_to: UUID | None = None
|
|
75
|
+
fence: conint(ge=1) | None = None
|
|
76
|
+
sent_at: AwareDatetime
|
|
77
|
+
expires_at: AwareDatetime | None = None
|
|
78
|
+
traceparent: constr(pattern=r"^00-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$") | None = None
|
|
79
|
+
body: dict[str, Any]
|
|
80
|
+
sig: Sig
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class V1ProofRecordIdGetResponse(BaseModel):
|
|
84
|
+
record_id: str
|
|
85
|
+
seal: str
|
|
86
|
+
proof: list[str]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Seal(BaseModel):
|
|
90
|
+
seal_id: str
|
|
91
|
+
prev: str
|
|
92
|
+
root: str
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class V1SealsGetResponse(BaseModel):
|
|
96
|
+
seals: list[Seal]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class V1RecordsGetResponse(BaseModel):
|
|
100
|
+
records: list[CityEvent]
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class V1ConversationsIdGetResponse(BaseModel):
|
|
104
|
+
envelopes: list[Envelope]
|