llm-api-scope 0.5.0__tar.gz → 0.5.1__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.
- {llm_api_scope-0.5.0/llm_api_scope.egg-info → llm_api_scope-0.5.1}/PKG-INFO +7 -2
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/README.md +6 -1
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/config.py +25 -1
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/openapi/app.py +15 -10
- llm_api_scope-0.5.1/apiscope/openapi/fetch.py +58 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/rfc/app.py +18 -4
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1/llm_api_scope.egg-info}/PKG-INFO +7 -2
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/pyproject.toml +1 -1
- llm_api_scope-0.5.0/apiscope/openapi/fetch.py +0 -49
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/LICENSE +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/__init__.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/main.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/openapi/__init__.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/openapi/reader.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/openapi/schema.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/openapi/spec/__init__.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/openapi/spec/app.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/openapi/spec/schema.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/rfc/__init__.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/rfc/fetch.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/rfc/parse_txt.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/rfc/parse_xml.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/rfc/schema.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/rfc/search.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/apiscope/schema.py +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/llm_api_scope.egg-info/SOURCES.txt +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/llm_api_scope.egg-info/dependency_links.txt +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/llm_api_scope.egg-info/entry_points.txt +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/llm_api_scope.egg-info/requires.txt +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/llm_api_scope.egg-info/top_level.txt +0 -0
- {llm_api_scope-0.5.0 → llm_api_scope-0.5.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llm-api-scope
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: read and cache structured documents from remote for LLM agents
|
|
5
5
|
Author-email: D7x7z49 <85430783+D7x7z49@users.noreply.github.com>
|
|
6
6
|
License: MIT License
|
|
@@ -73,9 +73,14 @@ browse OpenAPI specifications with subcommands for discovering, listing, and des
|
|
|
73
73
|
|
|
74
74
|
aliases let you register frequently used specs once and reference them by short name. fetching is transparent — local copies are cached for fast repeat access, and a proxy can be configured for restricted networks.
|
|
75
75
|
|
|
76
|
+
### rfc
|
|
77
|
+
|
|
78
|
+
read, search, and navigate RFC documents from the IETF.
|
|
79
|
+
|
|
80
|
+
the metadata index is mirrored once via rsync. individual text files are fetched on demand and cached locally. you can browse the table of contents, jump to a section (XML) or page (TXT), filter the index by status or source, and run keyword searches against fulltext content.
|
|
81
|
+
|
|
76
82
|
## future
|
|
77
83
|
|
|
78
|
-
- read RFC documents by number
|
|
79
84
|
- read academic papers from arxiv
|
|
80
85
|
- more formal document formats as the need arises
|
|
81
86
|
|
|
@@ -24,9 +24,14 @@ browse OpenAPI specifications with subcommands for discovering, listing, and des
|
|
|
24
24
|
|
|
25
25
|
aliases let you register frequently used specs once and reference them by short name. fetching is transparent — local copies are cached for fast repeat access, and a proxy can be configured for restricted networks.
|
|
26
26
|
|
|
27
|
+
### rfc
|
|
28
|
+
|
|
29
|
+
read, search, and navigate RFC documents from the IETF.
|
|
30
|
+
|
|
31
|
+
the metadata index is mirrored once via rsync. individual text files are fetched on demand and cached locally. you can browse the table of contents, jump to a section (XML) or page (TXT), filter the index by status or source, and run keyword searches against fulltext content.
|
|
32
|
+
|
|
27
33
|
## future
|
|
28
34
|
|
|
29
|
-
- read RFC documents by number
|
|
30
35
|
- read academic papers from arxiv
|
|
31
36
|
- more formal document formats as the need arises
|
|
32
37
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# apiscope/config.py
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import time
|
|
4
5
|
from contextlib import contextmanager
|
|
5
6
|
from os import environ
|
|
6
7
|
from pathlib import Path
|
|
@@ -27,13 +28,36 @@ CACHE_ROOT = DEFAULT_ROOT / "cache"
|
|
|
27
28
|
# ==============================================================================
|
|
28
29
|
|
|
29
30
|
|
|
30
|
-
class
|
|
31
|
+
class BaseConfig(BaseModel):
|
|
32
|
+
cache_ttl: int # seconds
|
|
33
|
+
|
|
34
|
+
def is_stale_since(self, timestamp: float) -> bool:
|
|
35
|
+
return time.time() - timestamp > self.cache_ttl
|
|
36
|
+
|
|
37
|
+
def is_stale_path(self, path: Path) -> bool:
|
|
38
|
+
if not path.exists():
|
|
39
|
+
return True
|
|
40
|
+
stat = path.stat()
|
|
41
|
+
latest = max(stat.st_mtime, stat.st_ctime)
|
|
42
|
+
return self.is_stale_since(latest)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class OpenapiConfig(BaseConfig):
|
|
31
46
|
proxy: str | None = Field(default=None)
|
|
32
47
|
alias: dict[str, str] = Field(default_factory=dict)
|
|
33
48
|
|
|
49
|
+
# override
|
|
50
|
+
cache_ttl: int = Field(default=60 * 60 * 24) # 1 day
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class RfcConfig(BaseConfig):
|
|
54
|
+
# override
|
|
55
|
+
cache_ttl: int = Field(default=60 * 60 * 24 * 7) # 1 week
|
|
56
|
+
|
|
34
57
|
|
|
35
58
|
class Config(BaseModel):
|
|
36
59
|
openapi: OpenapiConfig = Field(default_factory=OpenapiConfig)
|
|
60
|
+
rfc: RfcConfig = Field(default_factory=RfcConfig)
|
|
37
61
|
|
|
38
62
|
@classmethod
|
|
39
63
|
def read(cls, path: Path) -> "Config":
|
|
@@ -7,7 +7,7 @@ from typing import Any
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
9
|
from apiscope.config import CACHE_ROOT, Config
|
|
10
|
-
from apiscope.openapi.fetch import fetch_openapi_spec
|
|
10
|
+
from apiscope.openapi.fetch import fetch_openapi_spec, openapi_cache_path
|
|
11
11
|
from apiscope.openapi.reader import HttpMethod, OpenapiReader
|
|
12
12
|
from apiscope.openapi.schema import OpenapiCommandContext
|
|
13
13
|
from apiscope.openapi.spec import spec_app
|
|
@@ -26,16 +26,20 @@ def _resolve_source(alias_or_source: str, config: Config) -> str:
|
|
|
26
26
|
return alias_or_source
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def _load_reader(
|
|
30
|
-
|
|
29
|
+
def _load_reader(
|
|
30
|
+
source: str, cache_dir: Path, proxy: str | None = None, refresh: bool = False
|
|
31
|
+
) -> OpenapiReader:
|
|
32
|
+
cached = fetch_openapi_spec(source, cache_dir, proxy, refresh=refresh)
|
|
31
33
|
return OpenapiReader.load(cached)
|
|
32
34
|
|
|
33
35
|
|
|
34
|
-
def _get_reader(source: str, ctx: typer.Context) -> OpenapiReader:
|
|
36
|
+
def _get_reader(source: str, ctx: typer.Context, force: bool = False) -> OpenapiReader:
|
|
35
37
|
resolved = _resolve_source(source, ctx.obj.config)
|
|
36
38
|
cache_dir = ctx.obj.openapi_command_context.cache_dir
|
|
37
39
|
proxy = ctx.obj.config.openapi.proxy
|
|
38
|
-
|
|
40
|
+
cache_path = openapi_cache_path(resolved, cache_dir)
|
|
41
|
+
stale = ctx.obj.config.openapi.is_stale_path(cache_path)
|
|
42
|
+
return _load_reader(resolved, cache_dir, proxy, refresh=force or stale)
|
|
39
43
|
|
|
40
44
|
|
|
41
45
|
# ==============================================================================
|
|
@@ -61,9 +65,9 @@ def list_operations(
|
|
|
61
65
|
source: str = typer.Argument(help="alias or path to the OpenAPI spec"),
|
|
62
66
|
tag: str | None = typer.Option(default=None, help="filter by tag"),
|
|
63
67
|
method: str | None = typer.Option(default=None, help="filter by HTTP method"),
|
|
68
|
+
force: bool = typer.Option(False, "--force", help="force re-fetch ignoring cache"),
|
|
64
69
|
) -> None:
|
|
65
|
-
|
|
66
|
-
reader = _get_reader(source, ctx)
|
|
70
|
+
reader = _get_reader(source, ctx, force=force)
|
|
67
71
|
|
|
68
72
|
# collect all operations (path + method + identity fields)
|
|
69
73
|
operations: list[dict[str, Any]] = []
|
|
@@ -106,9 +110,9 @@ def describe_operation(
|
|
|
106
110
|
request: bool = typer.Option(
|
|
107
111
|
default=False, show_default=False, help="show only request fields, omit responses"
|
|
108
112
|
),
|
|
113
|
+
force: bool = typer.Option(False, "--force", help="force re-fetch ignoring cache"),
|
|
109
114
|
) -> None:
|
|
110
|
-
|
|
111
|
-
reader = _get_reader(source, ctx)
|
|
115
|
+
reader = _get_reader(source, ctx, force=force)
|
|
112
116
|
|
|
113
117
|
# merge path-item parameters with operation parameters
|
|
114
118
|
path_item = reader.paths.get(path, {})
|
|
@@ -132,8 +136,9 @@ def describe_operation(
|
|
|
132
136
|
def show_info(
|
|
133
137
|
ctx: typer.Context,
|
|
134
138
|
source: str = typer.Argument(help="alias or path to the OpenAPI spec"),
|
|
139
|
+
force: bool = typer.Option(False, "--force", help="force re-fetch ignoring cache"),
|
|
135
140
|
) -> None:
|
|
136
|
-
reader = _get_reader(source, ctx)
|
|
141
|
+
reader = _get_reader(source, ctx, force=force)
|
|
137
142
|
|
|
138
143
|
META_KEYS = ("openapi", "info", "servers", "tags", "security", "externalDocs")
|
|
139
144
|
meta = {k: v for k, v in reader.raw.items() if k in META_KEYS}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# apiscope/openapi/fetch.py
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
from hashlib import sha256
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from urllib.parse import unquote, urlparse
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
|
|
10
|
+
OPENAPI_EXTENSIONS = {".json", ".yaml", ".yml"}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _cache_key(source: str) -> str:
|
|
14
|
+
return sha256(source.encode()).hexdigest()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def openapi_cache_path(source: str, cache_dir: Path) -> Path:
|
|
18
|
+
if "://" in source:
|
|
19
|
+
path = unquote(urlparse(source).path).rstrip()
|
|
20
|
+
else:
|
|
21
|
+
path = source
|
|
22
|
+
suffix = Path(path).suffix.lower()
|
|
23
|
+
ext = suffix if suffix in OPENAPI_EXTENSIONS else ".json"
|
|
24
|
+
return cache_dir / f"{_cache_key(source)}{ext}"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _fetch_local(source: str, cache_dir: Path, refresh: bool = False) -> Path:
|
|
28
|
+
cache_path = openapi_cache_path(source, cache_dir)
|
|
29
|
+
|
|
30
|
+
if refresh or not cache_path.exists():
|
|
31
|
+
src = Path(source).expanduser().resolve()
|
|
32
|
+
shutil.copy2(src, cache_path)
|
|
33
|
+
|
|
34
|
+
return cache_path
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _fetch_remote(
|
|
38
|
+
url: str, cache_dir: Path, proxy: str | None = None, refresh: bool = False
|
|
39
|
+
) -> Path:
|
|
40
|
+
cache_path = openapi_cache_path(url, cache_dir)
|
|
41
|
+
|
|
42
|
+
if refresh or not cache_path.exists():
|
|
43
|
+
client_kwargs: dict = {}
|
|
44
|
+
if proxy is not None:
|
|
45
|
+
client_kwargs["proxy"] = proxy
|
|
46
|
+
resp = httpx.get(url, follow_redirects=True, **client_kwargs)
|
|
47
|
+
resp.raise_for_status()
|
|
48
|
+
cache_path.write_bytes(resp.content)
|
|
49
|
+
|
|
50
|
+
return cache_path
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def fetch_openapi_spec(
|
|
54
|
+
source: str, cache_dir: Path, proxy: str | None = None, refresh: bool = False
|
|
55
|
+
) -> Path:
|
|
56
|
+
if "://" in source:
|
|
57
|
+
return _fetch_remote(source, cache_dir, proxy, refresh=refresh)
|
|
58
|
+
return _fetch_local(source, cache_dir, refresh=refresh)
|
|
@@ -88,10 +88,20 @@ def _show_page_info(content: str, number: int, rfc_ctx: RfcCommandContext) -> No
|
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
@app.command(name="sync", help="download RFC metadata index via rsync")
|
|
91
|
-
def sync_index(
|
|
91
|
+
def sync_index(
|
|
92
|
+
ctx: typer.Context,
|
|
93
|
+
force: bool = typer.Option(False, "--force", help="force re-sync ignoring cache TTL"),
|
|
94
|
+
) -> None:
|
|
92
95
|
rfc_ctx = ctx.obj.rfc_command_context
|
|
96
|
+
rfc_config = ctx.obj.config.rfc
|
|
97
|
+
index_dir = rfc_ctx.index_json_dir
|
|
98
|
+
|
|
99
|
+
if not force and not rfc_config.is_stale_path(index_dir):
|
|
100
|
+
typer.echo("index is up to date")
|
|
101
|
+
return
|
|
102
|
+
|
|
93
103
|
typer.echo("syncing via rsync...")
|
|
94
|
-
err = fetch_all_index_json(
|
|
104
|
+
err = fetch_all_index_json(index_dir)
|
|
95
105
|
if err is not None:
|
|
96
106
|
typer.echo(f"sync failed\n{err}", err=True)
|
|
97
107
|
raise typer.Exit(code=1)
|
|
@@ -139,8 +149,10 @@ def read_content(
|
|
|
139
149
|
section: str | None = typer.Option(None, "--section", help="extract by section id (XML only)"),
|
|
140
150
|
page: int | None = typer.Option(None, "--page", help="extract by page number (TXT only)"),
|
|
141
151
|
json_output: bool = typer.Option(False, "--json", help="output as JSON"),
|
|
152
|
+
force: bool = typer.Option(False, "--force", help="force re-fetch ignoring cache TTL"),
|
|
142
153
|
) -> None:
|
|
143
154
|
rfc_ctx = ctx.obj.rfc_command_context
|
|
155
|
+
rfc_config = ctx.obj.config.rfc
|
|
144
156
|
meta = rfc_ctx.get_index_json(number)
|
|
145
157
|
if meta is None:
|
|
146
158
|
typer.echo(f"rfc {number} not found", err=True)
|
|
@@ -158,7 +170,7 @@ def read_content(
|
|
|
158
170
|
raise typer.Exit(code=1)
|
|
159
171
|
|
|
160
172
|
# ensure content is available locally
|
|
161
|
-
if
|
|
173
|
+
if force or rfc_config.is_stale_path(content_path):
|
|
162
174
|
typer.echo(f"fetching <{content_path.name}> via rsync...")
|
|
163
175
|
err = fetch_content_by_number(number, content_path.parent, fmt)
|
|
164
176
|
if err is not None:
|
|
@@ -235,8 +247,10 @@ def search_rfc(
|
|
|
235
247
|
no_snippet: bool = typer.Option(False, "--no-snippet", help="hide content snippet"),
|
|
236
248
|
limit: int = typer.Option(20, "--limit", help="max results shown"),
|
|
237
249
|
offset: int = typer.Option(0, "--offset", help="skip first N results"),
|
|
250
|
+
force: bool = typer.Option(False, "--force", help="force re-fetch ignoring cache TTL"),
|
|
238
251
|
) -> None:
|
|
239
252
|
rfc_ctx = ctx.obj.rfc_command_context
|
|
253
|
+
rfc_config = ctx.obj.config.rfc
|
|
240
254
|
# step 1: dispatch mode
|
|
241
255
|
index_filters = [status, since, until, author, title, abstract, keywords_field, source]
|
|
242
256
|
fulltext_mode = number is not None and term is not None
|
|
@@ -272,7 +286,7 @@ def search_rfc(
|
|
|
272
286
|
raise typer.Exit(code=1)
|
|
273
287
|
|
|
274
288
|
content_path = rfc_ctx.get_content_txt_path(number)
|
|
275
|
-
if
|
|
289
|
+
if force or rfc_config.is_stale_path(content_path):
|
|
276
290
|
typer.echo(f"fetching <{content_path.name}> via rsync...")
|
|
277
291
|
err = fetch_content_by_number(number, content_path.parent, "txt")
|
|
278
292
|
if err is not None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llm-api-scope
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: read and cache structured documents from remote for LLM agents
|
|
5
5
|
Author-email: D7x7z49 <85430783+D7x7z49@users.noreply.github.com>
|
|
6
6
|
License: MIT License
|
|
@@ -73,9 +73,14 @@ browse OpenAPI specifications with subcommands for discovering, listing, and des
|
|
|
73
73
|
|
|
74
74
|
aliases let you register frequently used specs once and reference them by short name. fetching is transparent — local copies are cached for fast repeat access, and a proxy can be configured for restricted networks.
|
|
75
75
|
|
|
76
|
+
### rfc
|
|
77
|
+
|
|
78
|
+
read, search, and navigate RFC documents from the IETF.
|
|
79
|
+
|
|
80
|
+
the metadata index is mirrored once via rsync. individual text files are fetched on demand and cached locally. you can browse the table of contents, jump to a section (XML) or page (TXT), filter the index by status or source, and run keyword searches against fulltext content.
|
|
81
|
+
|
|
76
82
|
## future
|
|
77
83
|
|
|
78
|
-
- read RFC documents by number
|
|
79
84
|
- read academic papers from arxiv
|
|
80
85
|
- more formal document formats as the need arises
|
|
81
86
|
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# apiscope/openapi/fetch.py
|
|
2
|
-
|
|
3
|
-
import shutil
|
|
4
|
-
from hashlib import sha256
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from urllib.parse import unquote, urlparse
|
|
7
|
-
|
|
8
|
-
import httpx
|
|
9
|
-
|
|
10
|
-
OPENAPI_EXTENSIONS = {".json", ".yaml", ".yml"}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def _cache_key(source: str) -> str:
|
|
14
|
-
return sha256(source.encode()).hexdigest()
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def _fetch_local(source: str, cache_dir: Path) -> Path:
|
|
18
|
-
suffix = Path(source).suffix.lower()
|
|
19
|
-
ext = suffix if suffix in OPENAPI_EXTENSIONS else ".json"
|
|
20
|
-
cache_path = cache_dir / f"{_cache_key(source)}{ext}"
|
|
21
|
-
|
|
22
|
-
if not cache_path.exists():
|
|
23
|
-
src = Path(source).expanduser().resolve()
|
|
24
|
-
shutil.copy2(src, cache_path)
|
|
25
|
-
|
|
26
|
-
return cache_path
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def _fetch_remote(url: str, cache_dir: Path, proxy: str | None = None) -> Path:
|
|
30
|
-
path = unquote(urlparse(url).path).rstrip()
|
|
31
|
-
suffix = Path(path).suffix.lower()
|
|
32
|
-
ext = suffix if suffix in OPENAPI_EXTENSIONS else ".json"
|
|
33
|
-
cache_path = cache_dir / f"{_cache_key(url)}{ext}"
|
|
34
|
-
|
|
35
|
-
if not cache_path.exists():
|
|
36
|
-
client_kwargs: dict = {}
|
|
37
|
-
if proxy is not None:
|
|
38
|
-
client_kwargs["proxy"] = proxy
|
|
39
|
-
resp = httpx.get(url, follow_redirects=True, **client_kwargs)
|
|
40
|
-
resp.raise_for_status()
|
|
41
|
-
cache_path.write_bytes(resp.content)
|
|
42
|
-
|
|
43
|
-
return cache_path
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def fetch_openapi_spec(source: str, cache_dir: Path, proxy: str | None = None) -> Path:
|
|
47
|
-
if "://" in source:
|
|
48
|
-
return _fetch_remote(source, cache_dir, proxy)
|
|
49
|
-
return _fetch_local(source, cache_dir)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|