xmemcli 0.0.11__tar.gz → 0.0.12__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.
- xmemcli-0.0.11/README.md → xmemcli-0.0.12/PKG-INFO +17 -3
- xmemcli-0.0.11/PKG-INFO → xmemcli-0.0.12/README.md +9 -11
- {xmemcli-0.0.11 → xmemcli-0.0.12}/pyproject.toml +1 -1
- {xmemcli-0.0.11 → xmemcli-0.0.12}/uv.lock +1 -1
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/__init__.py +1 -1
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_output.py +6 -3
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_pretty.py +1 -1
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_rc.py +19 -21
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_version.py +1 -1
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/cli.py +27 -4
- {xmemcli-0.0.11 → xmemcli-0.0.12}/.gitignore +0 -0
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_binding.py +0 -0
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_binding_cmd.py +0 -0
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_context_cmd.py +0 -0
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_email_login.py +0 -0
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_envelope.py +0 -0
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_help.py +0 -0
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_instance_schema.py +0 -0
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_login.py +0 -0
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_mcp_bridge.py +0 -0
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_org_cluster.py +0 -0
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_text.py +0 -0
- {xmemcli-0.0.11 → xmemcli-0.0.12}/xmemcli/_xmem_bridge.py +0 -0
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: xmemcli
|
|
3
|
+
Version: 0.0.12
|
|
4
|
+
Summary: Command-line client for the xmemory HTTP API: PKCE auth, instances, read/write, schema and XMD tools.
|
|
5
|
+
Keywords: api,cli,llm,memory,xmemory
|
|
6
|
+
Requires-Python: <4,>=3.12
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
|
|
1
9
|
# `xmemcli` — xmemory command-line client
|
|
2
10
|
|
|
3
11
|
Single-command CLI for reading from, writing to, and managing xmemory instances over the HTTP API at `$XMEM_API_URL` (default `https://api.xmemory.ai`), using an API key from the Console UI or `xmemcli auth login`.
|
|
@@ -89,7 +97,7 @@ xmemcli version
|
|
|
89
97
|
xmemcli auth login
|
|
90
98
|
```
|
|
91
99
|
|
|
92
|
-
Version: **0.0.
|
|
100
|
+
Version: **0.0.12**.
|
|
93
101
|
|
|
94
102
|
|
|
95
103
|
---
|
|
@@ -153,6 +161,8 @@ A credential that already resolves (via `/whoami`) to the requested address at t
|
|
|
153
161
|
|
|
154
162
|
Project-local **`.xmemrc.json`**: `{email, api_url, api_key}`. Found by walking up from `--rc-dir` / CWD, ceiling `$HOME`.
|
|
155
163
|
|
|
164
|
+
Those three keys are required and must be strings. Anything else in the file is **ignored, not rejected** (since 0.0.12): the rc is the only durable credential store, so a file this CLI cannot load is a CLI that cannot run any command at all — which would make adding a field to the file a breaking change far wider than the feature that added it.
|
|
165
|
+
|
|
156
166
|
Resolution order: `--api-key` → `$XMEM_API_KEY` → `.xmemrc.json`. Origin: `--api-url` → `$XMEM_API_URL` → rc `api_url` → default — **except** when the key is explicit (`--api-key` / `$XMEM_API_KEY`): the rc's `api_url` is skipped and the origin falls back to the built-in default unless you also set `--api-url` / `$XMEM_API_URL`. The localhost-key-at-prod guard applies when the key comes from the rc and an explicit `--api-url` / `$XMEM_API_URL` mismatches that rc origin.
|
|
157
167
|
|
|
158
168
|
## Identity — `/whoami`
|
|
@@ -183,7 +193,7 @@ Each login binds its own port and names it in the login URL, so concurrent login
|
|
|
183
193
|
|
|
184
194
|
# `xmemcli` — capabilities reference
|
|
185
195
|
|
|
186
|
-
**Version:** `0.0.
|
|
196
|
+
**Version:** `0.0.12` (see `xmemcli version`)
|
|
187
197
|
|
|
188
198
|
`xmemcli` is the command-line client for the **xmemory HTTP API**. It is **human-approved for key acquisition**: an AI-assisted IDE may run reads, writes, and inventory, and may initiate `auth login`, but a key is only issued after one explicit human approval — a click in the Console UI (PKCE handoff) or an approval of a sign-in email (`--email`). Quota limits change only in the **Console UI**. The full acquisition contract is in the auth-flow reference bundled with this package.
|
|
189
199
|
|
|
@@ -505,7 +515,9 @@ Some commands attach a more specific `stage` (`validate`, `usage`, `limit`, …)
|
|
|
505
515
|
|
|
506
516
|
**Breaking change in 0.0.5:** HTTP **403** previously exited **4** (`auth`); it now exits **3** (`permission`). Scripts that treated exit **4** as “any auth-layer failure” must check `stage` or handle exit **3** for permission denials.
|
|
507
517
|
|
|
508
|
-
|
|
518
|
+
**Compatibility change in 0.0.12:** an `.xmemrc.json` carrying a key this release does not recognise is now **accepted**, and the unknown key ignored; `email`, `api_url`, and `api_key` are still required and still have to be strings. Until 0.0.12 the key set was closed, which made adding a field to the file a breaking change for **every** command rather than for the one that would read it — a CLI that cannot load the rc cannot resolve a credential for anything. Nothing writes an extra key yet; this release exists so that a later one can.
|
|
519
|
+
|
|
520
|
+
See also [`ACCOUNTS_AUTH.md`](../docs/auth/ACCOUNTS_AUTH.md) for the server-side 401/403/404 rules.
|
|
509
521
|
|
|
510
522
|
---
|
|
511
523
|
|
|
@@ -594,6 +606,8 @@ These apply **before** any subcommand (`xmemcli help options`).
|
|
|
594
606
|
|
|
595
607
|
**Credential resolution:** `--api-key` → `$XMEM_API_KEY` → nearest `.xmemrc.json` → *Not logged in — run `xmemcli auth login` first.*
|
|
596
608
|
|
|
609
|
+
**`.xmemrc.json` shape:** `{email, api_url, api_key}`, all three required and all three strings. Keys beyond those are ignored rather than rejected (see the 0.0.12 note above), so a file written by a newer CLI still loads here.
|
|
610
|
+
|
|
597
611
|
**`context` is the one exception, and it is a security boundary.** Every other command walks up
|
|
598
612
|
from the current directory for the nearest `.xmemrc.json`, which is deliberate: a person chose
|
|
599
613
|
that directory and ran the command in it. `context` has neither — a session-start hook runs it
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: xmemcli
|
|
3
|
-
Version: 0.0.11
|
|
4
|
-
Summary: Command-line client for the xmemory HTTP API: PKCE auth, instances, read/write, schema and XMD tools.
|
|
5
|
-
Keywords: api,cli,llm,memory,xmemory
|
|
6
|
-
Requires-Python: <4,>=3.12
|
|
7
|
-
Description-Content-Type: text/markdown
|
|
8
|
-
|
|
9
1
|
# `xmemcli` — xmemory command-line client
|
|
10
2
|
|
|
11
3
|
Single-command CLI for reading from, writing to, and managing xmemory instances over the HTTP API at `$XMEM_API_URL` (default `https://api.xmemory.ai`), using an API key from the Console UI or `xmemcli auth login`.
|
|
@@ -97,7 +89,7 @@ xmemcli version
|
|
|
97
89
|
xmemcli auth login
|
|
98
90
|
```
|
|
99
91
|
|
|
100
|
-
Version: **0.0.
|
|
92
|
+
Version: **0.0.12**.
|
|
101
93
|
|
|
102
94
|
|
|
103
95
|
---
|
|
@@ -161,6 +153,8 @@ A credential that already resolves (via `/whoami`) to the requested address at t
|
|
|
161
153
|
|
|
162
154
|
Project-local **`.xmemrc.json`**: `{email, api_url, api_key}`. Found by walking up from `--rc-dir` / CWD, ceiling `$HOME`.
|
|
163
155
|
|
|
156
|
+
Those three keys are required and must be strings. Anything else in the file is **ignored, not rejected** (since 0.0.12): the rc is the only durable credential store, so a file this CLI cannot load is a CLI that cannot run any command at all — which would make adding a field to the file a breaking change far wider than the feature that added it.
|
|
157
|
+
|
|
164
158
|
Resolution order: `--api-key` → `$XMEM_API_KEY` → `.xmemrc.json`. Origin: `--api-url` → `$XMEM_API_URL` → rc `api_url` → default — **except** when the key is explicit (`--api-key` / `$XMEM_API_KEY`): the rc's `api_url` is skipped and the origin falls back to the built-in default unless you also set `--api-url` / `$XMEM_API_URL`. The localhost-key-at-prod guard applies when the key comes from the rc and an explicit `--api-url` / `$XMEM_API_URL` mismatches that rc origin.
|
|
165
159
|
|
|
166
160
|
## Identity — `/whoami`
|
|
@@ -191,7 +185,7 @@ Each login binds its own port and names it in the login URL, so concurrent login
|
|
|
191
185
|
|
|
192
186
|
# `xmemcli` — capabilities reference
|
|
193
187
|
|
|
194
|
-
**Version:** `0.0.
|
|
188
|
+
**Version:** `0.0.12` (see `xmemcli version`)
|
|
195
189
|
|
|
196
190
|
`xmemcli` is the command-line client for the **xmemory HTTP API**. It is **human-approved for key acquisition**: an AI-assisted IDE may run reads, writes, and inventory, and may initiate `auth login`, but a key is only issued after one explicit human approval — a click in the Console UI (PKCE handoff) or an approval of a sign-in email (`--email`). Quota limits change only in the **Console UI**. The full acquisition contract is in the auth-flow reference bundled with this package.
|
|
197
191
|
|
|
@@ -513,7 +507,9 @@ Some commands attach a more specific `stage` (`validate`, `usage`, `limit`, …)
|
|
|
513
507
|
|
|
514
508
|
**Breaking change in 0.0.5:** HTTP **403** previously exited **4** (`auth`); it now exits **3** (`permission`). Scripts that treated exit **4** as “any auth-layer failure” must check `stage` or handle exit **3** for permission denials.
|
|
515
509
|
|
|
516
|
-
|
|
510
|
+
**Compatibility change in 0.0.12:** an `.xmemrc.json` carrying a key this release does not recognise is now **accepted**, and the unknown key ignored; `email`, `api_url`, and `api_key` are still required and still have to be strings. Until 0.0.12 the key set was closed, which made adding a field to the file a breaking change for **every** command rather than for the one that would read it — a CLI that cannot load the rc cannot resolve a credential for anything. Nothing writes an extra key yet; this release exists so that a later one can.
|
|
511
|
+
|
|
512
|
+
See also [`ACCOUNTS_AUTH.md`](../docs/auth/ACCOUNTS_AUTH.md) for the server-side 401/403/404 rules.
|
|
517
513
|
|
|
518
514
|
---
|
|
519
515
|
|
|
@@ -602,6 +598,8 @@ These apply **before** any subcommand (`xmemcli help options`).
|
|
|
602
598
|
|
|
603
599
|
**Credential resolution:** `--api-key` → `$XMEM_API_KEY` → nearest `.xmemrc.json` → *Not logged in — run `xmemcli auth login` first.*
|
|
604
600
|
|
|
601
|
+
**`.xmemrc.json` shape:** `{email, api_url, api_key}`, all three required and all three strings. Keys beyond those are ignored rather than rejected (see the 0.0.12 note above), so a file written by a newer CLI still loads here.
|
|
602
|
+
|
|
605
603
|
**`context` is the one exception, and it is a security boundary.** Every other command walks up
|
|
606
604
|
from the current directory for the nearest `.xmemrc.json`, which is deliberate: a person chose
|
|
607
605
|
that directory and ran the command in it. `context` has neither — a session-start hook runs it
|
|
@@ -84,9 +84,12 @@ class OutputKind(StrEnum):
|
|
|
84
84
|
#: payload: ``write_id``; the caller must poll.
|
|
85
85
|
WRITE_ASYNC = "write_async"
|
|
86
86
|
|
|
87
|
-
#:
|
|
88
|
-
#:
|
|
89
|
-
#: ``
|
|
87
|
+
#: Result of an ``async_write_status`` poll. Curated payload: ``write_id`` + ``write_status``
|
|
88
|
+
#: — any ``WriteQueueStatus`` value, not only the terminal ones, plus the CLI's own
|
|
89
|
+
#: ``"error"`` / ``"unknown"`` fallbacks — whichever of ``submitted_at`` / ``started_at`` /
|
|
90
|
+
#: ``completed_at`` is set, ``restart_count`` once non-zero (``--verbose`` keeps a zero count and
|
|
91
|
+
#: drops only the null an unknown ``write_id`` reports), and ``error_detail`` / ``error``
|
|
92
|
+
#: / ``http_status`` / ``timed_out`` when applicable.
|
|
90
93
|
WRITE_STATUS = "write_status"
|
|
91
94
|
|
|
92
95
|
#: Read in ``mode=single-answer``. Curated payload: ``answer`` (falls
|
|
@@ -78,7 +78,7 @@ def _render_write_async(payload: Any, stream: IO[str]) -> None:
|
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
def _render_write_status(payload: Any, stream: IO[str]) -> None:
|
|
81
|
-
"""
|
|
81
|
+
"""Poll result — short status line, no body; the timing fields stay JSON-only."""
|
|
82
82
|
if not isinstance(payload, dict):
|
|
83
83
|
_default_handler(payload, stream)
|
|
84
84
|
return
|
|
@@ -5,9 +5,11 @@ The CLI resolves its API key and API origin from a **project-local**
|
|
|
5
5
|
(or the current directory). The search does **not** stop at the repository root
|
|
6
6
|
and is **ceilinged at ``$HOME``** — ``~/.xmemrc.json`` is the last fallback.
|
|
7
7
|
|
|
8
|
-
The file is stdlib JSON,
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
The file is stdlib JSON, one identity per file: ``{email, api_url, api_key}``.
|
|
9
|
+
Those three are required and must be strings; a key this release does not know is
|
|
10
|
+
ignored rather than rejected, so a later release can record something new in the
|
|
11
|
+
file without every older CLI refusing to run. The ``0600`` atomic writer is used
|
|
12
|
+
by ``xmem auth login``; see ``cli/AUTH-FLOW.md``.
|
|
11
13
|
|
|
12
14
|
Resolution precedence (applied by ``cli/xmem.py``): ``--api-key`` → ``$XMEM_API_KEY`` → ``.xmemrc.json``; and
|
|
13
15
|
``--api-url`` → ``$XMEM_API_URL`` → the rc's ``api_url`` → the built-in default — **with one escape hatch**: when the
|
|
@@ -26,9 +28,10 @@ from typing import NamedTuple
|
|
|
26
28
|
RC_FILENAME = ".xmemrc.json"
|
|
27
29
|
ENV_RC_DIR = "XMEM_RC_DIR"
|
|
28
30
|
|
|
29
|
-
# The
|
|
30
|
-
#
|
|
31
|
-
#
|
|
31
|
+
# The keys an `.xmemrc.json` must contain. Required, not exhaustive: anything else in the file is ignored.
|
|
32
|
+
# The set was closed until 0.0.12, on the grounds that a typo'd or extended file should fail loudly.
|
|
33
|
+
# It made adding a field a breaking change for every command rather than for the one that would read it,
|
|
34
|
+
# because a CLI that cannot load the rc cannot resolve a credential for anything.
|
|
32
35
|
_RC_KEYS = ("email", "api_url", "api_key")
|
|
33
36
|
|
|
34
37
|
|
|
@@ -90,27 +93,22 @@ def find_rc(start_dir: Path) -> Path | None:
|
|
|
90
93
|
|
|
91
94
|
|
|
92
95
|
def load_rc(path: Path) -> RcRecord:
|
|
93
|
-
"""Parse and validate an ``.xmemrc.json``; raise ``RcError`` if malformed.
|
|
96
|
+
"""Parse and validate an ``.xmemrc.json``; raise ``RcError`` if malformed.
|
|
97
|
+
|
|
98
|
+
"Required keys present", not "exactly these keys": an unrecognised key is ignored
|
|
99
|
+
whatever its type. Type-checking a field nothing here reads is how a later release's
|
|
100
|
+
richer value becomes this release's hard failure — having no opinion about it is the
|
|
101
|
+
whole point of ignoring it.
|
|
102
|
+
"""
|
|
94
103
|
try:
|
|
95
104
|
raw = json.loads(path.read_text(encoding="utf-8"))
|
|
96
105
|
except (OSError, json.JSONDecodeError) as exc:
|
|
97
106
|
raise RcError(f"{path}: could not read JSON ({exc})") from exc
|
|
98
107
|
if not isinstance(raw, dict):
|
|
99
108
|
raise RcError(f"{path}: expected a JSON object, got {type(raw).__name__}")
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
missing = sorted(expected - keys)
|
|
104
|
-
extra = sorted(keys - expected)
|
|
105
|
-
detail = ", ".join(
|
|
106
|
-
part
|
|
107
|
-
for part in (
|
|
108
|
-
f"missing {missing}" if missing else "",
|
|
109
|
-
f"unexpected {extra}" if extra else "",
|
|
110
|
-
)
|
|
111
|
-
if part
|
|
112
|
-
)
|
|
113
|
-
raise RcError(f"{path}: must contain exactly {list(_RC_KEYS)} ({detail})")
|
|
109
|
+
missing = sorted(set(_RC_KEYS) - set(raw.keys()))
|
|
110
|
+
if missing:
|
|
111
|
+
raise RcError(f"{path}: must contain {list(_RC_KEYS)} (missing {missing})")
|
|
114
112
|
for key in _RC_KEYS:
|
|
115
113
|
if not isinstance(raw[key], str):
|
|
116
114
|
raise RcError(f"{path}: '{key}' must be a string")
|
|
@@ -30,13 +30,16 @@ Curated output
|
|
|
30
30
|
The CLI does NOT echo the raw API payload. Every subcommand reduces the
|
|
31
31
|
server response to the fields the user actually asked for (see the
|
|
32
32
|
``_curate_*`` helpers): a read returns the answer / rows, a write returns
|
|
33
|
-
its ``write_id``, a status poll returns ``write_id`` + ``write_status
|
|
33
|
+
its ``write_id``, a status poll returns ``write_id`` + ``write_status``
|
|
34
|
+
plus whichever timing fields the server set (and the restart count once
|
|
35
|
+
it is non-zero).
|
|
34
36
|
Server internals — ``sql``, ``diff_plan``, the raw ``cleaned_objects``
|
|
35
37
|
tree — are never emitted, in either output mode. ``pending_suggestions``
|
|
36
38
|
(a count of schema-evolution proposals waiting for review) is kept on
|
|
37
39
|
read responses so agents know when to run ``schema suggestions review``.
|
|
38
40
|
``--verbose`` adds *extra* (still non-internal) context: ``trace_id`` and
|
|
39
|
-
``console_url``, when the server provided them
|
|
41
|
+
``console_url``, when the server provided them, and keeps a zero
|
|
42
|
+
``restart_count`` a status poll would otherwise curate away.
|
|
40
43
|
|
|
41
44
|
Sync vs async writes
|
|
42
45
|
--------------------
|
|
@@ -749,10 +752,26 @@ def _curate_write_status(item: dict[str, Any], verbose: bool) -> dict[str, Any]:
|
|
|
749
752
|
"write_status": item.get("write_status", "unknown"),
|
|
750
753
|
}
|
|
751
754
|
# Carry the failure / bookkeeping fields only when they say something.
|
|
752
|
-
for key in (
|
|
755
|
+
for key in (
|
|
756
|
+
"error_detail",
|
|
757
|
+
"submitted_at",
|
|
758
|
+
"started_at",
|
|
759
|
+
"completed_at",
|
|
760
|
+
"error",
|
|
761
|
+
"http_status",
|
|
762
|
+
"timed_out",
|
|
763
|
+
):
|
|
753
764
|
value = item.get(key)
|
|
754
765
|
if value is not None:
|
|
755
766
|
curated[key] = value
|
|
767
|
+
# Never-restarted is the norm, so a zero count is noise on the default output; show it there only when
|
|
768
|
+
# the write was restarted. Under --verbose the 0 is kept and only the null is dropped: the server sends
|
|
769
|
+
# null exactly for an unknown write_id, and printing that as 0 would let a caller aggregating polls count
|
|
770
|
+
# a row that does not exist as a write that ran clean.
|
|
771
|
+
restart_count = item.get("restart_count")
|
|
772
|
+
show_restart_count = restart_count is not None if verbose else bool(restart_count)
|
|
773
|
+
if show_restart_count:
|
|
774
|
+
curated["restart_count"] = restart_count
|
|
756
775
|
return _add_verbose_extras(curated, item, verbose)
|
|
757
776
|
|
|
758
777
|
|
|
@@ -1122,6 +1141,9 @@ def cmd_write_status(args: argparse.Namespace) -> tuple[CliOutput, int]:
|
|
|
1122
1141
|
}
|
|
1123
1142
|
last_status = item.get("write_status", "unknown")
|
|
1124
1143
|
last_body = item
|
|
1144
|
+
# Mirrors the terminal tail of WriteQueueStatus (completed, failed, not_found). Hand-written
|
|
1145
|
+
# rather than derived: this CLI is stdlib-only by design and imports nothing from the server
|
|
1146
|
+
# packages, so the enum is out of reach here.
|
|
1125
1147
|
if last_status in ("completed", "failed", "not_found"):
|
|
1126
1148
|
return item
|
|
1127
1149
|
if time.monotonic() >= deadline:
|
|
@@ -1444,6 +1466,7 @@ def _cmd_auth_login_email(args: argparse.Namespace) -> tuple[CliOutput, int]:
|
|
|
1444
1466
|
),
|
|
1445
1467
|
EXIT_HTTP_ERROR,
|
|
1446
1468
|
)
|
|
1469
|
+
|
|
1447
1470
|
explicit_api = _explicit_api_url(args)
|
|
1448
1471
|
rc_origin = rc.api_url.strip() if rc is not None and rc.api_url.strip() else None
|
|
1449
1472
|
api_origin = explicit_api or rc_origin or DEFAULT_API_URL
|
|
@@ -1641,7 +1664,7 @@ def _cmd_auth_login_email(args: argparse.Namespace) -> tuple[CliOutput, int]:
|
|
|
1641
1664
|
detail = getattr(exc, "strerror", None) or type(exc).__name__
|
|
1642
1665
|
return (
|
|
1643
1666
|
make_error(
|
|
1644
|
-
f
|
|
1667
|
+
f'could not write {RC_FILENAME} ({detail}). The minted key "{result.key_name}" '
|
|
1645
1668
|
f"(prefix {result.key_prefix}…) is active — revoke it in the Console if it should not stay.",
|
|
1646
1669
|
stage="login",
|
|
1647
1670
|
),
|
|
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
|