email-cli-tool 0.3.0__tar.gz → 0.3.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.
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/PKG-INFO +3 -3
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/README.md +2 -2
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/README_CN.md +2 -2
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/pyproject.toml +1 -1
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/src/emailcli/cli.py +1 -1
- email_cli_tool-0.3.1/src/emailcli/skill_install.py +71 -0
- email_cli_tool-0.3.1/src/emailcli/skills/wait-email/SKILL.md +60 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/tests/test_skill_install.py +29 -14
- email_cli_tool-0.3.0/src/emailcli/skill_install.py +0 -65
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/.github/workflows/publish.yml +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/.github/workflows/test.yml +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/.gitignore +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/.python-version +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/LICENSE +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/docs/superpowers/plans/2026-03-25-emailcli.md +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/docs/superpowers/plans/2026-06-10-emailcli-skill-install.md +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/docs/superpowers/specs/2026-03-25-emailcli-design.md +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/docs/superpowers/specs/2026-06-10-emailcli-skill-install-design.md +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/src/emailcli/__init__.py +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/src/emailcli/config.py +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/src/emailcli/exceptions.py +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/src/emailcli/message.py +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/src/emailcli/py.typed +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/src/emailcli/receiver.py +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/src/emailcli/sender.py +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/src/emailcli/skills/send-email/SKILL.md +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/tests/__init__.py +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/tests/test_cli.py +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/tests/test_config.py +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/tests/test_message.py +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/tests/test_receiver.py +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/tests/test_sender.py +0 -0
- {email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: email-cli-tool
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: A simple CLI tool for sending emails with plain text, HTML, and file attachments
|
|
5
5
|
Project-URL: Homepage, https://github.com/ClaymanTwinkle/email-cli-tool
|
|
6
6
|
Project-URL: Repository, https://github.com/ClaymanTwinkle/email-cli-tool
|
|
@@ -158,7 +158,7 @@ Display current configuration (password is masked).
|
|
|
158
158
|
|
|
159
159
|
### `emailcli skill install`
|
|
160
160
|
|
|
161
|
-
Install the `send-email` agent
|
|
161
|
+
Install the `send-email` and `wait-email` agent skills so Claude Code or Codex can send and receive mail for you.
|
|
162
162
|
|
|
163
163
|
```bash
|
|
164
164
|
# Install for both Claude Code and Codex (default)
|
|
@@ -169,7 +169,7 @@ emailcli skill install --target claude
|
|
|
169
169
|
emailcli skill install --target codex
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
Writes `SKILL.md`
|
|
172
|
+
Writes each skill's `SKILL.md` under `~/.claude/skills/` and/or `~/.codex/skills/`.
|
|
173
173
|
|
|
174
174
|
## Configuration
|
|
175
175
|
|
|
@@ -132,7 +132,7 @@ Display current configuration (password is masked).
|
|
|
132
132
|
|
|
133
133
|
### `emailcli skill install`
|
|
134
134
|
|
|
135
|
-
Install the `send-email` agent
|
|
135
|
+
Install the `send-email` and `wait-email` agent skills so Claude Code or Codex can send and receive mail for you.
|
|
136
136
|
|
|
137
137
|
```bash
|
|
138
138
|
# Install for both Claude Code and Codex (default)
|
|
@@ -143,7 +143,7 @@ emailcli skill install --target claude
|
|
|
143
143
|
emailcli skill install --target codex
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
-
Writes `SKILL.md`
|
|
146
|
+
Writes each skill's `SKILL.md` under `~/.claude/skills/` and/or `~/.codex/skills/`.
|
|
147
147
|
|
|
148
148
|
## Configuration
|
|
149
149
|
|
|
@@ -132,7 +132,7 @@ emailcli watch --timeout 300 --save-attachments ./downloads
|
|
|
132
132
|
|
|
133
133
|
### `emailcli skill install`
|
|
134
134
|
|
|
135
|
-
安装 `send-email` 技能,让 Claude Code 或 Codex
|
|
135
|
+
安装 `send-email` 和 `wait-email` 技能,让 Claude Code 或 Codex 可以直接帮你收发邮件。
|
|
136
136
|
|
|
137
137
|
```bash
|
|
138
138
|
# 默认同时安装到 Claude Code 和 Codex
|
|
@@ -143,7 +143,7 @@ emailcli skill install --target claude
|
|
|
143
143
|
emailcli skill install --target codex
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
会把各技能的 `SKILL.md` 写入 `~/.claude/skills/` 和/或 `~/.codex/skills/` 下的同名目录。
|
|
147
147
|
|
|
148
148
|
## 配置文件
|
|
149
149
|
|
|
@@ -252,7 +252,7 @@ def skill_group():
|
|
|
252
252
|
)
|
|
253
253
|
@click.option("--home", default=None, type=click.Path(), hidden=True, help="Home directory (for testing).")
|
|
254
254
|
def install(target, home):
|
|
255
|
-
"""Install the
|
|
255
|
+
"""Install the emailcli agent skills into Claude Code and/or Codex."""
|
|
256
256
|
home_dir = Path(home) if home else Path.home()
|
|
257
257
|
targets = ["claude", "codex"] if target == "all" else [target]
|
|
258
258
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from importlib.resources import files
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
SKILL_NAMES = ["send-email", "wait-email"]
|
|
7
|
+
|
|
8
|
+
# target name -> home subdirectory that holds the agent's skills
|
|
9
|
+
TARGET_HOME_DIRS = {
|
|
10
|
+
"claude": ".claude",
|
|
11
|
+
"codex": ".codex",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
# human-facing label per target
|
|
15
|
+
TARGET_LABELS = {
|
|
16
|
+
"claude": "Claude",
|
|
17
|
+
"codex": "Codex",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def load_skill_content(skill_name: str) -> str:
|
|
22
|
+
"""Read a bundled SKILL.md shipped as package data."""
|
|
23
|
+
return (
|
|
24
|
+
files("emailcli")
|
|
25
|
+
.joinpath(f"skills/{skill_name}/SKILL.md")
|
|
26
|
+
.read_text(encoding="utf-8")
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def skill_dest(home: Path, target: str, skill_name: str) -> Path:
|
|
31
|
+
"""Destination SKILL.md path for a target under the given home dir."""
|
|
32
|
+
return home / TARGET_HOME_DIRS[target] / "skills" / skill_name / "SKILL.md"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class InstallResult:
|
|
37
|
+
target: str
|
|
38
|
+
skill: str
|
|
39
|
+
path: Path
|
|
40
|
+
status: Literal["created", "updated", "failed"]
|
|
41
|
+
error: str | None = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def install_skill(home: Path, targets: list[str]) -> list[InstallResult]:
|
|
45
|
+
"""Write the bundled skills into each target's user-level skills dir.
|
|
46
|
+
|
|
47
|
+
Each (target, skill) pair is handled independently; a write failure on
|
|
48
|
+
one does not stop the others.
|
|
49
|
+
"""
|
|
50
|
+
unknown = [t for t in targets if t not in TARGET_HOME_DIRS]
|
|
51
|
+
if unknown:
|
|
52
|
+
raise ValueError(f"Unknown target(s): {', '.join(unknown)}")
|
|
53
|
+
contents = {name: load_skill_content(name) for name in SKILL_NAMES}
|
|
54
|
+
results: list[InstallResult] = []
|
|
55
|
+
for target in targets:
|
|
56
|
+
for skill_name in SKILL_NAMES:
|
|
57
|
+
dest = skill_dest(home, target, skill_name)
|
|
58
|
+
try:
|
|
59
|
+
existed = dest.exists()
|
|
60
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
61
|
+
dest.write_text(contents[skill_name], encoding="utf-8")
|
|
62
|
+
results.append(
|
|
63
|
+
InstallResult(
|
|
64
|
+
target, skill_name, dest, "updated" if existed else "created"
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
except OSError as exc:
|
|
68
|
+
results.append(
|
|
69
|
+
InstallResult(target, skill_name, dest, "failed", str(exc))
|
|
70
|
+
)
|
|
71
|
+
return results
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wait-email
|
|
3
|
+
description: "Wait for the next incoming email via emailcli. Prints sender/subject/body and downloads attachments. Use when the user wants to wait for, receive, or read an incoming email."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Wait for Email
|
|
7
|
+
|
|
8
|
+
Wait for the next incoming email from Claude Code using the `emailcli watch` command.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
Invoke with `/wait-email` followed by natural language describing what to wait for:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
/wait-email 等下一封新邮件
|
|
16
|
+
/wait-email 等 5 分钟,附件存到 ./downloads
|
|
17
|
+
/wait-email 等一封验证码邮件,把验证码读出来
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Workflow
|
|
21
|
+
|
|
22
|
+
### 1. Parse user intent
|
|
23
|
+
|
|
24
|
+
Extract from the user's message:
|
|
25
|
+
- **Timeout** (`--timeout`): seconds to wait. Default to 300 if the user gives no limit; use 0 (wait forever) only if the user explicitly asks to wait indefinitely.
|
|
26
|
+
- **Attachment directory** (`--save-attachments`): where to save attachments, if the user mentions them.
|
|
27
|
+
- **Mailbox** (`--mailbox`): defaults to INBOX.
|
|
28
|
+
|
|
29
|
+
### 2. Run the command
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
emailcli watch --timeout 300 --save-attachments ./downloads
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- The command blocks until an email arrives, so set the Bash tool timeout comfortably larger than `--timeout` (or run it in the background for long waits).
|
|
36
|
+
- Headers and body go to stdout; progress messages and saved-attachment paths go to stderr.
|
|
37
|
+
- Only emails arriving **after** the command starts are matched; existing unread mail is ignored.
|
|
38
|
+
|
|
39
|
+
### 3. Report result
|
|
40
|
+
|
|
41
|
+
By exit code:
|
|
42
|
+
- **0** — email received: report From / Subject / Date, the body (or the specific detail the user asked for, e.g. a verification code), and any saved attachment paths.
|
|
43
|
+
- **2** — timed out with no new email: tell the user.
|
|
44
|
+
- **1** — error: show the message. If it says IMAP settings are missing, tell the user to re-run `emailcli init` or add an `imap:` section to `~/.emailcli/config.yaml`.
|
|
45
|
+
|
|
46
|
+
## Command Reference
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
emailcli watch \
|
|
50
|
+
[--timeout SECONDS] # 0 = wait forever
|
|
51
|
+
[--poll-interval SECONDS] # default: 10
|
|
52
|
+
[--save-attachments DIR] \
|
|
53
|
+
[--mailbox NAME] # default: INBOX
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Important
|
|
57
|
+
|
|
58
|
+
- Requires email-cli-tool **0.3.0+** with IMAP configured (`emailcli init`).
|
|
59
|
+
- The received email is marked as read on the server.
|
|
60
|
+
- Do NOT ask for confirmation — start waiting once inputs are clear, and tell the user you are waiting.
|
|
@@ -5,26 +5,38 @@ import pytest
|
|
|
5
5
|
from click.testing import CliRunner
|
|
6
6
|
|
|
7
7
|
from emailcli.cli import cli
|
|
8
|
-
from emailcli.skill_install import
|
|
8
|
+
from emailcli.skill_install import (
|
|
9
|
+
SKILL_NAMES,
|
|
10
|
+
InstallResult,
|
|
11
|
+
install_skill,
|
|
12
|
+
load_skill_content,
|
|
13
|
+
)
|
|
9
14
|
|
|
10
15
|
|
|
11
|
-
def
|
|
16
|
+
def test_bundled_send_email_skill_is_packaged():
|
|
12
17
|
content = files("emailcli").joinpath("skills/send-email/SKILL.md").read_text(encoding="utf-8")
|
|
13
18
|
assert content.startswith("---")
|
|
14
19
|
assert "name: send-email" in content
|
|
15
20
|
assert "emailcli send" in content
|
|
16
21
|
|
|
17
22
|
|
|
23
|
+
def test_bundled_wait_email_skill_is_packaged():
|
|
24
|
+
content = files("emailcli").joinpath("skills/wait-email/SKILL.md").read_text(encoding="utf-8")
|
|
25
|
+
assert content.startswith("---")
|
|
26
|
+
assert "name: wait-email" in content
|
|
27
|
+
assert "emailcli watch" in content
|
|
28
|
+
|
|
29
|
+
|
|
18
30
|
def test_install_all_writes_both_targets(tmp_path):
|
|
19
31
|
results = install_skill(tmp_path, ["claude", "codex"])
|
|
20
32
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
assert
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
for target_dir in (".claude", ".codex"):
|
|
34
|
+
for skill in SKILL_NAMES:
|
|
35
|
+
path = tmp_path / target_dir / "skills" / skill / "SKILL.md"
|
|
36
|
+
assert path.read_text(encoding="utf-8") == load_skill_content(skill)
|
|
37
|
+
assert {(r.target, r.skill) for r in results} == {
|
|
38
|
+
(t, s) for t in ("claude", "codex") for s in SKILL_NAMES
|
|
39
|
+
}
|
|
28
40
|
assert all(r.status == "created" for r in results)
|
|
29
41
|
|
|
30
42
|
|
|
@@ -32,6 +44,7 @@ def test_install_claude_only(tmp_path):
|
|
|
32
44
|
install_skill(tmp_path, ["claude"])
|
|
33
45
|
|
|
34
46
|
assert (tmp_path / ".claude" / "skills" / "send-email" / "SKILL.md").exists()
|
|
47
|
+
assert (tmp_path / ".claude" / "skills" / "wait-email" / "SKILL.md").exists()
|
|
35
48
|
assert not (tmp_path / ".codex").exists()
|
|
36
49
|
|
|
37
50
|
|
|
@@ -39,9 +52,9 @@ def test_install_is_idempotent(tmp_path):
|
|
|
39
52
|
install_skill(tmp_path, ["claude"])
|
|
40
53
|
results = install_skill(tmp_path, ["claude"])
|
|
41
54
|
|
|
42
|
-
assert
|
|
55
|
+
assert all(r.status == "updated" for r in results)
|
|
43
56
|
path = tmp_path / ".claude" / "skills" / "send-email" / "SKILL.md"
|
|
44
|
-
assert path.read_text(encoding="utf-8") == load_skill_content()
|
|
57
|
+
assert path.read_text(encoding="utf-8") == load_skill_content("send-email")
|
|
45
58
|
|
|
46
59
|
|
|
47
60
|
def test_install_unknown_target_raises(tmp_path):
|
|
@@ -54,8 +67,9 @@ def test_cli_skill_install_default_writes_both(tmp_path):
|
|
|
54
67
|
result = runner.invoke(cli, ["skill", "install", "--home", str(tmp_path)])
|
|
55
68
|
|
|
56
69
|
assert result.exit_code == 0
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
for target_dir in (".claude", ".codex"):
|
|
71
|
+
for skill in SKILL_NAMES:
|
|
72
|
+
assert (tmp_path / target_dir / "skills" / skill / "SKILL.md").exists()
|
|
59
73
|
assert "Claude" in result.output
|
|
60
74
|
assert "Codex" in result.output
|
|
61
75
|
|
|
@@ -68,6 +82,7 @@ def test_cli_skill_install_target_codex(tmp_path):
|
|
|
68
82
|
|
|
69
83
|
assert result.exit_code == 0
|
|
70
84
|
assert (tmp_path / ".codex" / "skills" / "send-email" / "SKILL.md").exists()
|
|
85
|
+
assert (tmp_path / ".codex" / "skills" / "wait-email" / "SKILL.md").exists()
|
|
71
86
|
assert not (tmp_path / ".claude").exists()
|
|
72
87
|
|
|
73
88
|
|
|
@@ -81,7 +96,7 @@ def test_cli_skill_install_invalid_target(tmp_path):
|
|
|
81
96
|
|
|
82
97
|
|
|
83
98
|
def test_cli_skill_install_failure_exit_code(tmp_path):
|
|
84
|
-
failed = [InstallResult("claude", tmp_path / "SKILL.md", "failed", "boom")]
|
|
99
|
+
failed = [InstallResult("claude", "send-email", tmp_path / "SKILL.md", "failed", "boom")]
|
|
85
100
|
runner = CliRunner()
|
|
86
101
|
with patch("emailcli.skill_install.install_skill", return_value=failed):
|
|
87
102
|
result = runner.invoke(cli, ["skill", "install", "--home", str(tmp_path)])
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from importlib.resources import files
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import Literal
|
|
5
|
-
|
|
6
|
-
SKILL_NAME = "send-email"
|
|
7
|
-
|
|
8
|
-
# target name -> home subdirectory that holds the agent's skills
|
|
9
|
-
TARGET_HOME_DIRS = {
|
|
10
|
-
"claude": ".claude",
|
|
11
|
-
"codex": ".codex",
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
# human-facing label per target
|
|
15
|
-
TARGET_LABELS = {
|
|
16
|
-
"claude": "Claude",
|
|
17
|
-
"codex": "Codex",
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def load_skill_content() -> str:
|
|
22
|
-
"""Read the bundled SKILL.md shipped as package data."""
|
|
23
|
-
return (
|
|
24
|
-
files("emailcli")
|
|
25
|
-
.joinpath(f"skills/{SKILL_NAME}/SKILL.md")
|
|
26
|
-
.read_text(encoding="utf-8")
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def skill_dest(home: Path, target: str) -> Path:
|
|
31
|
-
"""Destination SKILL.md path for a target under the given home dir."""
|
|
32
|
-
return home / TARGET_HOME_DIRS[target] / "skills" / SKILL_NAME / "SKILL.md"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@dataclass
|
|
36
|
-
class InstallResult:
|
|
37
|
-
target: str
|
|
38
|
-
path: Path
|
|
39
|
-
status: Literal["created", "updated", "failed"]
|
|
40
|
-
error: str | None = None
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def install_skill(home: Path, targets: list[str]) -> list[InstallResult]:
|
|
44
|
-
"""Write the bundled skill into each target's user-level skills dir.
|
|
45
|
-
|
|
46
|
-
Each target is handled independently; a write failure on one target is
|
|
47
|
-
recorded and does not stop the others.
|
|
48
|
-
"""
|
|
49
|
-
unknown = [t for t in targets if t not in TARGET_HOME_DIRS]
|
|
50
|
-
if unknown:
|
|
51
|
-
raise ValueError(f"Unknown target(s): {', '.join(unknown)}")
|
|
52
|
-
content = load_skill_content()
|
|
53
|
-
results: list[InstallResult] = []
|
|
54
|
-
for target in targets:
|
|
55
|
-
dest = skill_dest(home, target)
|
|
56
|
-
try:
|
|
57
|
-
existed = dest.exists()
|
|
58
|
-
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
59
|
-
dest.write_text(content, encoding="utf-8")
|
|
60
|
-
results.append(
|
|
61
|
-
InstallResult(target, dest, "updated" if existed else "created")
|
|
62
|
-
)
|
|
63
|
-
except OSError as exc:
|
|
64
|
-
results.append(InstallResult(target, dest, "failed", str(exc)))
|
|
65
|
-
return results
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{email_cli_tool-0.3.0 → email_cli_tool-0.3.1}/docs/superpowers/specs/2026-03-25-emailcli-design.md
RENAMED
|
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
|