aimailsdk 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.
Files changed (45) hide show
  1. aimail/__init__.py +175 -0
  2. aimail/_aimail_bootstrap.py +87 -0
  3. aimail/_resources_release.py +91 -0
  4. aimail/aimail_base.py +1248 -0
  5. aimail/aimail_board.py +193 -0
  6. aimail/aimail_tools.py +1316 -0
  7. aimail/amail_mcp_server.py +302 -0
  8. aimail/deer-flow/aimail_inbound.py +171 -0
  9. aimail/deer-flow/amail_base.py +186 -0
  10. aimail/deer-flow/manage.py +710 -0
  11. aimail/gateway_api.py +177 -0
  12. aimail/hermes/aimail_hermes.py +1023 -0
  13. aimail/hermes/ensure_config.py +148 -0
  14. aimail/hermes/patch_profiles.py +350 -0
  15. aimail/hermes/patch_webhook.py +624 -0
  16. aimail/hermes/register_profiles.py +116 -0
  17. aimail/hermes/toolsets.py +132 -0
  18. aimail/install.py +433 -0
  19. aimail/openclaw/amail_base.py +353 -0
  20. aimail/resources/board/role_prompt_en/common.md +33 -0
  21. aimail/resources/board/role_prompt_en/orchestrator.md +43 -0
  22. aimail/resources/board/role_prompt_en/role_calibrator.md +27 -0
  23. aimail/resources/board/role_prompt_en/verifier.md +47 -0
  24. aimail/resources/board/role_prompt_en/whoami.md +24 -0
  25. aimail/resources/board/role_prompt_en/worker.md +48 -0
  26. aimail/resources/board/role_prompt_zh/common.md +33 -0
  27. aimail/resources/board/role_prompt_zh/orchestrator.md +43 -0
  28. aimail/resources/board/role_prompt_zh/role_calibrator.md +27 -0
  29. aimail/resources/board/role_prompt_zh/verifier.md +47 -0
  30. aimail/resources/board/role_prompt_zh/whoami.md +24 -0
  31. aimail/resources/board/role_prompt_zh/worker.md +48 -0
  32. aimail/resources/board/role_soul_en/Orchestrator.md +24 -0
  33. aimail/resources/board/role_soul_en/Owner.md +23 -0
  34. aimail/resources/board/role_soul_en/Verifier.md +23 -0
  35. aimail/resources/board/role_soul_en/Worker.md +23 -0
  36. aimail/resources/board/role_soul_zh/Orchestrator.md +24 -0
  37. aimail/resources/board/role_soul_zh/Owner.md +25 -0
  38. aimail/resources/board/role_soul_zh/Verifier.md +23 -0
  39. aimail/resources/board/role_soul_zh/Worker.md +23 -0
  40. aimail/resources/skills/DESCRIPTION.md +3 -0
  41. aimail/resources/skills/SKILL.md +231 -0
  42. aimailsdk-0.1.0.dist-info/METADATA +125 -0
  43. aimailsdk-0.1.0.dist-info/RECORD +45 -0
  44. aimailsdk-0.1.0.dist-info/WHEEL +4 -0
  45. aimailsdk-0.1.0.dist-info/licenses/COPYING +674 -0
aimail/__init__.py ADDED
@@ -0,0 +1,175 @@
1
+ # -*- coding: utf-8 -*-
2
+ """aimail — AIMail runtime SDK (Python). Unified public entry point.
3
+
4
+ This package is the single public surface for integrating aimail: both the
5
+ platform hosts (Hermes / OpenClaw / DeerFlow) and third-party agents should
6
+ ``import aimail`` and use the re-exported API, rather than each doing its own
7
+ ``sys.path`` bootstrap + flat-script imports.
8
+
9
+ Unified usage (recommended — hosts AND third parties):
10
+
11
+ import aimail
12
+ aimail.send_mail(to="x@example.com", subject="hi", body="hello")
13
+ client = aimail.GatewayClient(aimail.agent_email(), api_key="...")
14
+ aimail.manage_contacts(action="list")
15
+
16
+ The runtime core is implemented as flat scripts (``aimail_base.py``,
17
+ ``aimail_tools.py``, ...) that import each other by top-level name, so they
18
+ must sit on ``sys.path``. ``import aimail`` does that bootstrap for you and
19
+ re-exports the curated public API below. The legacy flat path (insert
20
+ ``aimail.core_dir()`` on ``sys.path`` then ``import aimail_tools``) still
21
+ works and is what the host adapters' bootstrap relies on — it is preserved,
22
+ not replaced.
23
+
24
+ Module layout inside the wheel mirrors the repository ``pysdk/`` directory
25
+ (single source of truth), so the runtime's flat imports keep working:
26
+
27
+ aimail/
28
+ aimail_base.py shared core (platform-agnostic)
29
+ aimail_tools.py shared core (GatewayClient / send_mail)
30
+ aimail_board.py shared core (A2A board)
31
+ gateway_api.py standard amail API client
32
+ amail_mcp_server.py platform-agnostic MCP server (stdio JSON-RPC)
33
+ _aimail_bootstrap.py location-agnostic sys.path bootstrap (runtime glue)
34
+ install.py self-contained install/uninstall entry
35
+ _resources_release.py board/skills resource release (install-time)
36
+ hermes/ Hermes adapter (6 modules, host-injected registry)
37
+ openclaw/ OpenClaw adapter (amail_base)
38
+ deer-flow/ DeerFlow adapter (inbound router / manage / base)
39
+ resources/
40
+ skills/ aimail SKILL.md + DESCRIPTION.md
41
+ board/role_prompt_en|zh, role_soul_en|zh board templates
42
+ """
43
+
44
+ import os as _os
45
+ import sys as _sys
46
+
47
+ __version__ = "0.1.0"
48
+
49
+ __all__ = [
50
+ "__version__",
51
+ # path helpers
52
+ "root",
53
+ "core_dir",
54
+ "skills_dir",
55
+ "board_role_prompt_dir",
56
+ "mcp_server_path",
57
+ # identity
58
+ "agent_email",
59
+ # outbound
60
+ "send_mail",
61
+ "GatewayClient",
62
+ # contacts
63
+ "manage_contacts",
64
+ "contact_profile",
65
+ "set_contact_profile",
66
+ # email
67
+ "email_summary",
68
+ "set_email_summary",
69
+ "store_inbound_message",
70
+ "render_message",
71
+ ]
72
+
73
+
74
+ # ── Path helpers ──────────────────────────────────────────────────────────
75
+
76
+ def root() -> str:
77
+ """Absolute path of this package directory."""
78
+ return _os.path.dirname(_os.path.abspath(__file__))
79
+
80
+
81
+ def core_dir() -> str:
82
+ """Directory holding the flat-script core modules (aimail_base.py ...).
83
+
84
+ Insert this on ``sys.path`` to enable the legacy flat imports::
85
+
86
+ import sys, aimail
87
+ sys.path.insert(0, aimail.core_dir())
88
+ import aimail_base
89
+
90
+ In an installed wheel the flat modules are force-included into the package
91
+ dir, so this equals ``root()``. In a raw repo checkout pysdk/ is the
92
+ single source of truth and mirrors the wheel layout 1:1, so this equals
93
+ ``root()`` there as well. Either way, the returned dir is guaranteed to
94
+ contain ``aimail_base.py``.
95
+ """
96
+ return _resolve_core_dir()
97
+
98
+
99
+ def skills_dir() -> str:
100
+ """aimail SKILL.md / DESCRIPTION.md directory."""
101
+ return _os.path.join(root(), "resources", "skills")
102
+
103
+
104
+ def board_role_prompt_dir() -> str:
105
+ """Board role prompt templates (English, default) directory."""
106
+ return _os.path.join(root(), "resources", "board", "role_prompt_en")
107
+
108
+
109
+ def mcp_server_path() -> str:
110
+ """Path of the platform-agnostic MCP server entry script."""
111
+ return _os.path.join(root(), "amail_mcp_server.py")
112
+
113
+
114
+ # ── Unified core bootstrap + re-export ────────────────────────────────────
115
+
116
+ def _resolve_core_dir() -> str:
117
+ """Locate the directory containing the flat core modules.
118
+
119
+ Resolution order (most specific first):
120
+ 1. installed layout / repo pysdk — flat modules sit directly in the
121
+ package dir (pysdk/ is the single source of truth; the wheel
122
+ mirrors it 1:1 inside site-packages/aimail/)
123
+ 2. AIMAIL_RUNTIME_DIR env override (explicit)
124
+
125
+ Returns the first candidate that contains ``aimail_base.py``, falling
126
+ back to ``root()`` (best effort) if none match.
127
+ """
128
+ here = root()
129
+ # 1. installed / repo pysdk layout: core modules are flat siblings here.
130
+ if _os.path.isfile(_os.path.join(here, "aimail_base.py")):
131
+ return here
132
+ # 2. explicit override.
133
+ env = _os.environ.get("AIMAIL_RUNTIME_DIR", "").strip()
134
+ if env:
135
+ d = _os.path.expanduser(env)
136
+ if _os.path.isfile(_os.path.join(d, "aimail_base.py")):
137
+ return d
138
+ return here
139
+
140
+
141
+ def _import_core():
142
+ """Idempotently put the core dir on sys.path and import the flat core
143
+ modules as top-level modules (the same module objects the host adapters
144
+ use — no dual-module identity problem). Returns (base, tools, board, api).
145
+ """
146
+ core = _resolve_core_dir()
147
+ if core not in _sys.path:
148
+ _sys.path.insert(0, core)
149
+ # Reuse the single-source runtime bootstrap so any entry-point layout
150
+ # (adapter subdirs, env override) is handled consistently.
151
+ try:
152
+ import _aimail_bootstrap as _b
153
+ _b.ensure_core(core)
154
+ except Exception:
155
+ pass # core already on path from the insert above; bootstrap is glue.
156
+ import aimail_base as _base
157
+ import aimail_tools as _tools
158
+ import aimail_board as _board
159
+ import gateway_api as _api
160
+ return _base, _tools, _board, _api
161
+
162
+
163
+ _base, _tools, _board, _api = _import_core()
164
+
165
+ # Re-export the curated public API (single public entry point).
166
+ send_mail = _tools.send_mail
167
+ GatewayClient = _tools._GatewayClient
168
+ agent_email = _tools._resolve_agent_email
169
+ manage_contacts = _tools.manage_contacts
170
+ contact_profile = _tools.contact_profile
171
+ set_contact_profile = _tools.set_contact_profile
172
+ email_summary = _tools.email_summary
173
+ set_email_summary = _tools.set_email_summary
174
+ store_inbound_message = _tools.store_inbound_message
175
+ render_message = _base.render_message
@@ -0,0 +1,87 @@
1
+ # -*- coding: utf-8 -*-
2
+ """_aimail_bootstrap — runtime core location resolution (single source).
3
+
4
+ Runtime modules are flat-script style (``import aimail_base``) and must
5
+ run from any location: the source repository (tools/), an installed bundle
6
+ (~/.aimail/... provisioner copies), or site-packages (pip aimail).
7
+ This module resolves the directory containing the shared core modules
8
+ (aimail_base.py / aimail_tools.py / aimail_board.py /
9
+ gateway_api.py) and puts it — plus the calling entry point's own
10
+ directory, for sibling imports — on sys.path.
11
+
12
+ Resolution order (most specific first):
13
+ 1. AIMAIL_RUNTIME_DIR env var (explicit override)
14
+ 2. the directory above the entry point (bundle / site-packages layout:
15
+ adapter dirs openclaw|deer-flow sit directly under the core dir)
16
+ 3. the entry point's own directory (flat layout: entry lives in core)
17
+
18
+ The repo checkout is deliberately NOT a path fallback: installed runtimes
19
+ must be self-contained (bundles) or installed (pip). Development from the
20
+ repo works via case 3, which is local by definition.
21
+
22
+ Entry points use it like:
23
+
24
+ def _amail_bootstrap():
25
+ import importlib.util as _ilu
26
+ _here = os.path.dirname(os.path.abspath(__file__))
27
+ for _d in (_here, os.path.dirname(_here)):
28
+ _p = os.path.join(_d, "_aimail_bootstrap.py")
29
+ if os.path.isfile(_p):
30
+ _spec = _ilu.spec_from_file_location("_aimail_bootstrap", _p)
31
+ _m = _ilu.module_from_spec(_spec)
32
+ sys.modules["_aimail_bootstrap"] = _m
33
+ _spec.loader.exec_module(_m)
34
+ _m.ensure_core(_here)
35
+ return
36
+ raise ImportError("aimail runtime core not found — set AIMAIL_RUNTIME_DIR")
37
+
38
+ _amail_bootstrap()
39
+
40
+ import aimail_base as _base # noqa: E402
41
+ """
42
+
43
+ import os
44
+ import sys
45
+
46
+
47
+ def _core_ok(d):
48
+ return bool(d) and os.path.isfile(os.path.join(d, "aimail_base.py"))
49
+
50
+
51
+ def ensure_core(self_dir=None):
52
+ """Put the core module dir (and the entry point's own dir) on sys.path.
53
+
54
+ Returns the resolved core dir, or None if no candidate exists.
55
+ Idempotent: repeated calls do not duplicate entries.
56
+ """
57
+ self_dir = os.path.abspath(self_dir or os.path.dirname(os.path.abspath(__file__)))
58
+ here = self_dir
59
+
60
+ core = None
61
+ # 1. explicit override
62
+ env = os.environ.get("AIMAIL_RUNTIME_DIR", "").strip()
63
+ if env:
64
+ d = os.path.expanduser(env)
65
+ if _core_ok(d):
66
+ core = d
67
+ # 2. parent dir (adapter layout: core sits above openclaw|deer-flow)
68
+ if core is None and os.path.basename(here) in ("openclaw", "deer-flow", "hermes"):
69
+ d = os.path.dirname(here)
70
+ if _core_ok(d):
71
+ core = d
72
+ # 3. own dir (flat layout: entry point lives in the core dir)
73
+ if core is None and _core_ok(here):
74
+ core = here
75
+
76
+ if core is None:
77
+ return None
78
+
79
+ # adapter dir first (sibling imports), then core; dedupe both
80
+ for d in (here, core):
81
+ if d not in sys.path:
82
+ sys.path.insert(0, d)
83
+ return core
84
+
85
+
86
+ if __name__ == "__main__":
87
+ print(ensure_core())
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env python3
2
+ """_resources_release — SDK 资源的本地配置目录展开(python 版)。
3
+
4
+ 架构:资源(role_prompt/role_soul × en/zh + skills)是公共种子,随 SDK
5
+ 分发;安装/启动时释放到 ~/.aimail/systems/{sid}/board/ 供运行时读取
6
+ (pysdk 与 tssdk 运行时同路径)。只补缺失/更新的文件,绝不覆盖用户已在
7
+ 配置目录个性化过的内容。
8
+
9
+ 与 tssdk mail-core release-resources.ts、cli 旧 release-board-resources.sh
10
+ 同语义;本模块为 pip/repo 双形态的 python 实现。
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import os
15
+ import shutil
16
+ import sys
17
+
18
+ # 双形态自举:core 目录(含本模块与 aimail_base.py)即 _CORE
19
+ _CORE = os.path.dirname(os.path.abspath(__file__))
20
+
21
+ # 源子目录(包内 resources/board) → 配置目录目标子目录
22
+ _DIR_MAP = (
23
+ ("role_prompt_en", "role_prompt"),
24
+ ("role_prompt_zh", "role_prompt_zh"),
25
+ ("role_soul_en", "role_soul"),
26
+ ("role_soul_zh", "role_soul_zh"),
27
+ )
28
+
29
+ _AIMAIL_HOME = os.path.join(os.path.expanduser("~"), ".aimail")
30
+
31
+
32
+ def agentmail_home() -> str:
33
+ return os.environ.get("AIMAIL_HOME", "") or _AIMAIL_HOME
34
+
35
+
36
+ def resources_board_dir() -> str:
37
+ """包内 resources/board 目录(repo:pysdk/resources/board;pip:aimail/resources/board)。"""
38
+ return os.path.join(_CORE, "resources", "board")
39
+
40
+
41
+ def release_resources(system_id: str, board_root: str | None = None) -> dict:
42
+ """释放 board 资源到 ~/.aimail/systems/{sid}/board/(幂等)。"""
43
+ src_root = board_root or resources_board_dir()
44
+ board_dir = os.path.join(agentmail_home(), "systems", system_id, "board")
45
+ copied = 0
46
+ skipped = 0
47
+ for src_name, dst_name in _DIR_MAP:
48
+ src_dir = os.path.join(src_root, src_name)
49
+ if not os.path.isdir(src_dir):
50
+ continue
51
+ dst_dir = os.path.join(board_dir, dst_name)
52
+ os.makedirs(dst_dir, exist_ok=True)
53
+ for fname in sorted(os.listdir(src_dir)):
54
+ if not fname.endswith(".md"):
55
+ continue
56
+ src = os.path.join(src_dir, fname)
57
+ dst = os.path.join(dst_dir, fname)
58
+ if os.path.exists(dst):
59
+ if os.path.getmtime(dst) >= os.path.getmtime(src):
60
+ skipped += 1
61
+ continue
62
+ shutil.copy2(src, dst)
63
+ copied += 1
64
+ return {"board_dir": board_dir, "copied": copied, "skipped": skipped}
65
+
66
+
67
+ def release_all_systems(board_root: str | None = None) -> list:
68
+ """对 ~/.aimail/systems/ 下全部已有系统展开(单系统机器亦覆盖)。"""
69
+ systems_root = os.path.join(agentmail_home(), "systems")
70
+ if not os.path.isdir(systems_root):
71
+ return []
72
+ out = []
73
+ for ent in sorted(os.listdir(systems_root)):
74
+ p = os.path.join(systems_root, ent)
75
+ if os.path.isdir(p):
76
+ try:
77
+ out.append(release_resources(ent, board_root))
78
+ except Exception: # noqa: BLE001
79
+ pass
80
+ return out
81
+
82
+
83
+ if __name__ == "__main__":
84
+ # 便捷:python _resources_release.py [system_id...]
85
+ sids = sys.argv[1:] or sorted(
86
+ d for d in os.listdir(os.path.join(agentmail_home(), "systems"))
87
+ if os.path.isdir(os.path.join(agentmail_home(), "systems", d))
88
+ ) if os.path.isdir(os.path.join(agentmail_home(), "systems")) else []
89
+ for sid in sids:
90
+ r = release_resources(sid)
91
+ print(f"{sid}: {r['board_dir']} (copied {r['copied']}, kept {r['skipped']})")