fluidattacks_cloudflare_sdk 0.1.0__py3-none-any.whl
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.
- fluidattacks_cloudflare_sdk/__init__.py +17 -0
- fluidattacks_cloudflare_sdk/_client/__init__.py +19 -0
- fluidattacks_cloudflare_sdk/_client/_client.py +30 -0
- fluidattacks_cloudflare_sdk/_client/_core.py +23 -0
- fluidattacks_cloudflare_sdk/_logger.py +30 -0
- fluidattacks_cloudflare_sdk/ai_gateway/__init__.py +56 -0
- fluidattacks_cloudflare_sdk/ai_gateway/_client.py +69 -0
- fluidattacks_cloudflare_sdk/ai_gateway/_decode.py +38 -0
- fluidattacks_cloudflare_sdk/ai_gateway/core.py +98 -0
- fluidattacks_cloudflare_sdk/core.py +19 -0
- fluidattacks_cloudflare_sdk-0.1.0.dist-info/METADATA +10 -0
- fluidattacks_cloudflare_sdk-0.1.0.dist-info/RECORD +13 -0
- fluidattacks_cloudflare_sdk-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Cloudflare SDK for fluidattacks observes."""
|
|
2
|
+
|
|
3
|
+
from fa_purity import Unsafe
|
|
4
|
+
|
|
5
|
+
from fluidattacks_cloudflare_sdk._client import Token
|
|
6
|
+
from fluidattacks_cloudflare_sdk._logger import set_logger
|
|
7
|
+
from fluidattacks_cloudflare_sdk.core import AccountId, GatewayId
|
|
8
|
+
|
|
9
|
+
__version__ = "0.1.0"
|
|
10
|
+
|
|
11
|
+
Unsafe.compute(set_logger(__name__, __version__))
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"AccountId",
|
|
15
|
+
"GatewayId",
|
|
16
|
+
"Token",
|
|
17
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Private connection package for the Cloudflare SDK."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from fluidattacks_cloudflare_sdk._client._client import CloudflareClient
|
|
6
|
+
from fluidattacks_cloudflare_sdk._client._core import Client, Token
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class CloudflareClientFactory:
|
|
11
|
+
"""Factory for a Cloudflare Client."""
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def new(token: Token) -> Client:
|
|
15
|
+
"""Build a Client from a Token."""
|
|
16
|
+
return CloudflareClient.new(token).client
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__all__ = ["Client", "CloudflareClientFactory", "Token"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Private Cloudflare client; builds the public Client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from cloudflare import Cloudflare
|
|
9
|
+
|
|
10
|
+
from fluidattacks_cloudflare_sdk._client._core import Client
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from fluidattacks_cloudflare_sdk._client._core import Token
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class CloudflareClient:
|
|
18
|
+
"""Private class holding the raw Cloudflare connection."""
|
|
19
|
+
|
|
20
|
+
_cloudflare: Cloudflare
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def new(token: Token) -> CloudflareClient:
|
|
24
|
+
"""Build a CloudflareClient from a Token."""
|
|
25
|
+
return CloudflareClient(Cloudflare(api_token=token.raw))
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def client(self) -> Client:
|
|
29
|
+
"""Return the opaque Client."""
|
|
30
|
+
return Client(self._cloudflare)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Core types for the private Cloudflare connection package."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from cloudflare import Cloudflare
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class Token:
|
|
14
|
+
"""Cloudflare API token."""
|
|
15
|
+
|
|
16
|
+
raw: str
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class Client:
|
|
21
|
+
"""Opaque Cloudflare connection; raw field is SDK-internal."""
|
|
22
|
+
|
|
23
|
+
client: Cloudflare
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Logger configuration for the Cloudflare SDK."""
|
|
2
|
+
|
|
3
|
+
from fa_purity import (
|
|
4
|
+
Cmd,
|
|
5
|
+
)
|
|
6
|
+
from fluidattacks_utils_logger import (
|
|
7
|
+
set_main_log,
|
|
8
|
+
)
|
|
9
|
+
from fluidattacks_utils_logger.env import (
|
|
10
|
+
current_app_env,
|
|
11
|
+
observes_debug,
|
|
12
|
+
)
|
|
13
|
+
from fluidattacks_utils_logger.handlers import (
|
|
14
|
+
LoggingConf,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def set_logger(root_name: str, version: str) -> Cmd[None]:
|
|
19
|
+
"""Configure structured logging for the SDK."""
|
|
20
|
+
app_env = current_app_env()
|
|
21
|
+
debug = observes_debug()
|
|
22
|
+
conf = app_env.map(
|
|
23
|
+
lambda env: LoggingConf(
|
|
24
|
+
app_name="observes_cloudflare",
|
|
25
|
+
app_type="sdk",
|
|
26
|
+
app_version=version,
|
|
27
|
+
release_stage=env,
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
return debug.bind(lambda d: conf.bind(lambda c: set_main_log(root_name, c, d)))
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""AI Gateway sub-package for the Cloudflare SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from fluidattacks_cloudflare_sdk._client import CloudflareClientFactory
|
|
9
|
+
from fluidattacks_cloudflare_sdk.ai_gateway._client import list_logs
|
|
10
|
+
from fluidattacks_cloudflare_sdk.ai_gateway.core import (
|
|
11
|
+
AiGatewayClient,
|
|
12
|
+
EndDate,
|
|
13
|
+
LogRecord,
|
|
14
|
+
LogsOrderBy,
|
|
15
|
+
LogsQuery,
|
|
16
|
+
StartDate,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from fluidattacks_cloudflare_sdk._client import Client, Token
|
|
21
|
+
from fluidattacks_cloudflare_sdk.core import AccountId, GatewayId
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _from_client(
|
|
25
|
+
cloudflare: Client,
|
|
26
|
+
account_id: AccountId,
|
|
27
|
+
gateway_id: GatewayId,
|
|
28
|
+
) -> AiGatewayClient:
|
|
29
|
+
return AiGatewayClient(
|
|
30
|
+
list_logs=lambda q: list_logs(cloudflare, account_id, gateway_id, q),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class AiGatewayClientFactory:
|
|
36
|
+
"""Factory for AiGatewayClient."""
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def new(
|
|
40
|
+
token: Token,
|
|
41
|
+
account_id: AccountId,
|
|
42
|
+
gateway_id: GatewayId,
|
|
43
|
+
) -> AiGatewayClient:
|
|
44
|
+
"""Build an AiGatewayClient from a token and identifiers."""
|
|
45
|
+
return _from_client(CloudflareClientFactory.new(token), account_id, gateway_id)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
"AiGatewayClient",
|
|
50
|
+
"AiGatewayClientFactory",
|
|
51
|
+
"EndDate",
|
|
52
|
+
"LogRecord",
|
|
53
|
+
"LogsOrderBy",
|
|
54
|
+
"LogsQuery",
|
|
55
|
+
"StartDate",
|
|
56
|
+
]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from cloudflare import NOT_GIVEN
|
|
7
|
+
from fa_purity import Cmd, FrozenList, Result, ResultE, Unsafe, cast_exception
|
|
8
|
+
|
|
9
|
+
from fluidattacks_cloudflare_sdk.ai_gateway._decode import decode_log
|
|
10
|
+
from fluidattacks_cloudflare_sdk.ai_gateway.core import (
|
|
11
|
+
LogRecord,
|
|
12
|
+
LogsOrderBy,
|
|
13
|
+
LogsQuery,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from fluidattacks_cloudflare_sdk._client import Client
|
|
18
|
+
from fluidattacks_cloudflare_sdk.core import AccountId, GatewayId
|
|
19
|
+
|
|
20
|
+
LOG = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def list_logs(
|
|
24
|
+
cloudflare: Client,
|
|
25
|
+
account_id: AccountId,
|
|
26
|
+
gateway_id: GatewayId,
|
|
27
|
+
query: LogsQuery,
|
|
28
|
+
) -> Cmd[ResultE[FrozenList[LogRecord]]]:
|
|
29
|
+
_order = query.order_by.value_or(LogsOrderBy.default())
|
|
30
|
+
_start = query.start.map(lambda s: s.raw.date_time.strftime("%Y-%m-%dT%H:%M:%SZ")).value_or(
|
|
31
|
+
NOT_GIVEN
|
|
32
|
+
)
|
|
33
|
+
_end = query.end.map(lambda e: e.raw.date_time.strftime("%Y-%m-%dT%H:%M:%SZ")).value_or(
|
|
34
|
+
NOT_GIVEN
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def _action() -> ResultE[FrozenList[LogRecord]]:
|
|
38
|
+
try:
|
|
39
|
+
LOG.info("fetching ai gateway logs for gateway %s", gateway_id.raw)
|
|
40
|
+
first_page = cloudflare.client.ai_gateway.logs.list(
|
|
41
|
+
gateway_id.raw,
|
|
42
|
+
account_id=account_id.raw,
|
|
43
|
+
start_date=_start,
|
|
44
|
+
end_date=_end,
|
|
45
|
+
order_by=_order.field,
|
|
46
|
+
order_by_direction=_order.direction,
|
|
47
|
+
)
|
|
48
|
+
items: list[LogRecord] = []
|
|
49
|
+
for page in first_page.iter_pages():
|
|
50
|
+
page_records = page.result
|
|
51
|
+
items.extend(
|
|
52
|
+
decode_log(raw).alt(Unsafe.raise_exception).to_union() for raw in page_records
|
|
53
|
+
)
|
|
54
|
+
LOG.info(
|
|
55
|
+
"page fetched: %d records (%d total so far)",
|
|
56
|
+
len(page_records),
|
|
57
|
+
len(items),
|
|
58
|
+
)
|
|
59
|
+
LOG.info(
|
|
60
|
+
"finished: %d log records from gateway %s",
|
|
61
|
+
len(items),
|
|
62
|
+
gateway_id.raw,
|
|
63
|
+
)
|
|
64
|
+
return Result.success(tuple(items))
|
|
65
|
+
except Exception as err:
|
|
66
|
+
LOG.exception("failed to fetch ai gateway logs")
|
|
67
|
+
return Result.failure(err, FrozenList[LogRecord]).alt(cast_exception)
|
|
68
|
+
|
|
69
|
+
return Cmd.wrap_impure(_action)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from fa_purity import Maybe
|
|
6
|
+
from fa_purity.date_time import DatetimeUTC
|
|
7
|
+
|
|
8
|
+
from fluidattacks_cloudflare_sdk.ai_gateway.core import LogRecord
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from cloudflare.types.ai_gateway import LogListResponse
|
|
12
|
+
from fa_purity import ResultE
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def decode_log(raw: LogListResponse) -> ResultE[LogRecord]:
|
|
16
|
+
return DatetimeUTC.assert_utc(raw.created_at).map(
|
|
17
|
+
lambda created_at: LogRecord(
|
|
18
|
+
id=raw.id,
|
|
19
|
+
cached=raw.cached,
|
|
20
|
+
created_at=created_at,
|
|
21
|
+
duration=raw.duration,
|
|
22
|
+
model=raw.model,
|
|
23
|
+
path=raw.path,
|
|
24
|
+
provider=raw.provider,
|
|
25
|
+
success=raw.success,
|
|
26
|
+
tokens_in=Maybe.from_optional(raw.tokens_in),
|
|
27
|
+
tokens_out=Maybe.from_optional(raw.tokens_out),
|
|
28
|
+
cost=Maybe.from_optional(raw.cost),
|
|
29
|
+
custom_cost=Maybe.from_optional(raw.custom_cost),
|
|
30
|
+
metadata=Maybe.from_optional(raw.metadata),
|
|
31
|
+
model_type=Maybe.from_optional(raw.ai_model_type),
|
|
32
|
+
request_content_type=Maybe.from_optional(raw.request_content_type),
|
|
33
|
+
request_type=Maybe.from_optional(raw.request_type),
|
|
34
|
+
response_content_type=Maybe.from_optional(raw.response_content_type),
|
|
35
|
+
status_code=Maybe.from_optional(raw.status_code),
|
|
36
|
+
step=Maybe.from_optional(raw.step),
|
|
37
|
+
)
|
|
38
|
+
)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Domain types and client interface for the Cloudflare AI Gateway resource."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import TYPE_CHECKING, Literal
|
|
7
|
+
|
|
8
|
+
from fa_purity import Maybe
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
|
|
13
|
+
from fa_purity import Cmd, FrozenList, ResultE
|
|
14
|
+
from fa_purity.date_time import DatetimeUTC
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
LogsOrderByField = Literal["created_at", "provider", "model", "model_type", "success", "cached"]
|
|
18
|
+
LogsOrderByDirection = Literal["asc", "desc"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class StartDate:
|
|
23
|
+
"""Inclusive lower bound for AI Gateway log queries."""
|
|
24
|
+
|
|
25
|
+
raw: DatetimeUTC
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class EndDate:
|
|
30
|
+
"""Exclusive upper bound for AI Gateway log queries."""
|
|
31
|
+
|
|
32
|
+
raw: DatetimeUTC
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True)
|
|
36
|
+
class LogsOrderBy:
|
|
37
|
+
"""Ordering configuration for AI Gateway log queries."""
|
|
38
|
+
|
|
39
|
+
field: LogsOrderByField
|
|
40
|
+
direction: LogsOrderByDirection
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
def default() -> LogsOrderBy:
|
|
44
|
+
"""Default ordering: created_at ascending."""
|
|
45
|
+
return LogsOrderBy(field="created_at", direction="asc")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class LogsQuery:
|
|
50
|
+
"""Filter and ordering options for AI Gateway log queries."""
|
|
51
|
+
|
|
52
|
+
start: Maybe[StartDate]
|
|
53
|
+
end: Maybe[EndDate]
|
|
54
|
+
order_by: Maybe[LogsOrderBy]
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def all() -> LogsQuery:
|
|
58
|
+
"""Query with no filters and default ordering."""
|
|
59
|
+
return LogsQuery(
|
|
60
|
+
start=Maybe.empty(),
|
|
61
|
+
end=Maybe.empty(),
|
|
62
|
+
order_by=Maybe.some(LogsOrderBy.default()),
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True)
|
|
67
|
+
class LogRecord:
|
|
68
|
+
"""A single AI Gateway request log entry."""
|
|
69
|
+
|
|
70
|
+
id: str
|
|
71
|
+
cached: bool
|
|
72
|
+
created_at: DatetimeUTC
|
|
73
|
+
duration: int
|
|
74
|
+
model: str
|
|
75
|
+
path: str
|
|
76
|
+
provider: str
|
|
77
|
+
success: bool
|
|
78
|
+
tokens_in: Maybe[int]
|
|
79
|
+
tokens_out: Maybe[int]
|
|
80
|
+
cost: Maybe[float]
|
|
81
|
+
custom_cost: Maybe[bool]
|
|
82
|
+
metadata: Maybe[str]
|
|
83
|
+
model_type: Maybe[str]
|
|
84
|
+
request_content_type: Maybe[str]
|
|
85
|
+
request_type: Maybe[str]
|
|
86
|
+
response_content_type: Maybe[str]
|
|
87
|
+
status_code: Maybe[int]
|
|
88
|
+
step: Maybe[int]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True)
|
|
92
|
+
class AiGatewayClient:
|
|
93
|
+
"""Interface for Cloudflare AI Gateway operations."""
|
|
94
|
+
|
|
95
|
+
list_logs: Callable[
|
|
96
|
+
[LogsQuery],
|
|
97
|
+
Cmd[ResultE[FrozenList[LogRecord]]],
|
|
98
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Shared identifiers for the Cloudflare SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class AccountId:
|
|
10
|
+
"""Cloudflare account identifier."""
|
|
11
|
+
|
|
12
|
+
raw: str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class GatewayId:
|
|
17
|
+
"""Cloudflare AI Gateway identifier."""
|
|
18
|
+
|
|
19
|
+
raw: str
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fluidattacks_cloudflare_sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: cloudflare SDK
|
|
5
|
+
Author-email: Product Team <development@fluidattacks.com>
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: cloudflare >=4.0.0, <5.0.0
|
|
8
|
+
Requires-Dist: fa-purity >=2.5.0, <3.0.0
|
|
9
|
+
Requires-Dist: fluidattacks-etl-utils >=4.0.0, <5.0.0
|
|
10
|
+
Requires-Dist: fluidattacks-utils-logger >=3.0.0, <4.0.0
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
fluidattacks_cloudflare_sdk/__init__.py,sha256=KL6tU7dsWVlnUGTHSKjA1ShqunAoj7xfWQdClzAileM,394
|
|
2
|
+
fluidattacks_cloudflare_sdk/_logger.py,sha256=VT7XgefbXm816YCaqhFinc9jQ4YdBlEgGmuwiRblctA,770
|
|
3
|
+
fluidattacks_cloudflare_sdk/core.py,sha256=Xx23kBk89CWNwujUG0WWJVI4rqujjdoAHNCfYIxxEt4,319
|
|
4
|
+
fluidattacks_cloudflare_sdk/_client/__init__.py,sha256=v9RbjQM0J-1ep1mn5PS4p3QyZ-iYFQCA2gays0uqNOw,542
|
|
5
|
+
fluidattacks_cloudflare_sdk/_client/_client.py,sha256=aVXCWrnHfEV85TEPqm1BCJk9HQd7sdf54knQn2EwGCM,796
|
|
6
|
+
fluidattacks_cloudflare_sdk/_client/_core.py,sha256=0bL4mzNG5AASbaGWHukHiam1bsZFR_hOIIzy5qWHEgM,441
|
|
7
|
+
fluidattacks_cloudflare_sdk/ai_gateway/__init__.py,sha256=EeS4VE96M3_eUmnJ4zPLIduVHx_odt9Ay_RbzTi6kAc,1390
|
|
8
|
+
fluidattacks_cloudflare_sdk/ai_gateway/_client.py,sha256=27YwWF48d5rLUsuZtSZfaS-jsYLCGuQRcLnCeT-YrIk,2351
|
|
9
|
+
fluidattacks_cloudflare_sdk/ai_gateway/_decode.py,sha256=JGq2pI5nlQ_WzsXAULqcMleHiNLlLjiG209Q_W1WiDU,1424
|
|
10
|
+
fluidattacks_cloudflare_sdk/ai_gateway/core.py,sha256=E8deYCxjnxjU1dqy5iyQ8ndIeNwzAxnZGBW2bUOhzIo,2321
|
|
11
|
+
fluidattacks_cloudflare_sdk-0.1.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
12
|
+
fluidattacks_cloudflare_sdk-0.1.0.dist-info/METADATA,sha256=9RnmbIMJ_K4yAHl75E8XF_1LpKmYL2Ws-RP6RoUljFk,371
|
|
13
|
+
fluidattacks_cloudflare_sdk-0.1.0.dist-info/RECORD,,
|