Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
420 changes: 376 additions & 44 deletions spec/openapi.yaml

Large diffs are not rendered by default.

21 changes: 12 additions & 9 deletions src/mail/client/src/mail_client/admin_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"Daemons",
[
("daemon-list (dl)", "List daemons."),
("daemon-get (dg)", "Get a daemon by local address."),
("daemon-get (dg)", "Get a daemon by worker name."),
("daemon-post (dp)", "Create daemon credentials."),
("daemon-delete (dd)", "Delete daemon credentials."),
],
Expand Down Expand Up @@ -235,7 +235,7 @@ def build_parser() -> argparse.ArgumentParser:
daemon_list_p.set_defaults(func=cmd_daemon_list, cmd="daemon-list")

# command `daemon-get`
daemon_get_d = "get a specific daemon by local address on the MAIL server"
daemon_get_d = "get a specific daemon by worker name on the MAIL server"
daemon_get_p = subparsers.add_parser(
"daemon-get",
aliases=["dg"],
Expand All @@ -244,7 +244,7 @@ def build_parser() -> argparse.ArgumentParser:
description=daemon_get_d,
)
daemon_get_p.add_argument(
"local_address", help="the local address of the daemon to get (daemon@swarm)"
"worker_name", help="the worker name of the daemon to get"
)
daemon_get_p.set_defaults(func=cmd_daemon_get, cmd="daemon-get")

Expand Down Expand Up @@ -459,7 +459,8 @@ def build_parser() -> argparse.ArgumentParser:
description=list_get_d,
)
list_get_p.add_argument(
"list_address", help="the address of the mailing list to get"
"list_address",
help="the local address of the mailing list to get (name@swarm)",
)
list_get_p.set_defaults(func=cmd_list_get_admin, cmd="list-get")

Expand Down Expand Up @@ -507,7 +508,8 @@ def build_parser() -> argparse.ArgumentParser:
description=list_delete_d,
)
list_delete_p.add_argument(
"list_address", help="the address of the mailing list to delete"
"list_address",
help="the local address of the mailing list to delete (name@swarm)",
)
list_delete_p.set_defaults(func=cmd_list_delete, cmd="list-delete")

Expand All @@ -523,11 +525,12 @@ def build_parser() -> argparse.ArgumentParser:
description=list_member_post_d,
)
list_member_post_p.add_argument(
"list_address", help="the MAIL address of the mailing list to add a member to"
"list_address",
help="the local address of the mailing list to add a member to (name@swarm)",
)
list_member_post_p.add_argument(
"member_address",
help="the MAIL address of the member to add to this mailing list",
help="the full MAIL address of the member to add to this mailing list",
)
list_member_post_p.set_defaults(func=cmd_list_member_post, cmd="list-member-post")

Expand All @@ -544,11 +547,11 @@ def build_parser() -> argparse.ArgumentParser:
)
list_member_delete_p.add_argument(
"list_address",
help="the MAIL address of the mailing list to remove a member from",
help="the local address of the mailing list to remove a member from (name@swarm)",
)
list_member_delete_p.add_argument(
"member_address",
help="the MAIL address of the member to remove from this mailing list",
help="the full MAIL address of the member to remove from this mailing list",
)
list_member_delete_p.set_defaults(
func=cmd_list_member_delete, cmd="list-member-delete"
Expand Down
8 changes: 5 additions & 3 deletions src/mail/client/src/mail_client/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ def build_parser() -> argparse.ArgumentParser:
description=list_get_d,
)
list_get_p.add_argument(
"list_address", help="the address of the mailing list to get"
"list_address", help="the local address of the mailing list to get (name@swarm)"
)
list_get_p.set_defaults(func=cmd_list_get, cmd="list-get")

Expand All @@ -502,7 +502,8 @@ def build_parser() -> argparse.ArgumentParser:
description=list_subscribe_d,
)
list_subscribe_p.add_argument(
"list_address", help="the address of the mailing list to subscribe to"
"list_address",
help="the local address of the mailing list to subscribe to (name@swarm)",
)
list_subscribe_p.set_defaults(func=cmd_list_subscribe, cmd="list-subscribe")

Expand All @@ -516,7 +517,8 @@ def build_parser() -> argparse.ArgumentParser:
description=list_unsubscribe_d,
)
list_unsubscribe_p.add_argument(
"list_address", help="the address of the mailing list to unsubscribe from"
"list_address",
help="the local address of the mailing list to unsubscribe from (name@swarm)",
)
list_unsubscribe_p.set_defaults(func=cmd_list_unsubscribe, cmd="list-unsubscribe")

Expand Down
6 changes: 3 additions & 3 deletions src/mail/client/src/mail_client/commands/daemon_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

def cmd_daemon_get(args: Namespace) -> None:
"""
Get a specific daemon by local address on the MAIL server.
Get a specific daemon by worker name on the MAIL server.
"""

# 1. check that required env vars are provided
Expand All @@ -24,9 +24,9 @@ def cmd_daemon_get(args: Namespace) -> None:
if MAIL_TOKEN is None:
raise ValueError("environment variable MAIL_TOKEN is required")

# 2. Attempt to get the specific daemon by local address on the MAIL server
# 2. Attempt to get the specific daemon by worker name on the MAIL server
response = httpx.get(
url=f"{MAIL_SERVER}/admin/daemons/{args.local_address}",
url=f"{MAIL_SERVER}/admin/daemons/{args.worker_name}",
headers={
"User-Agent": "Multi-Agent-Interface-Layer-CLI-Client/2.0.0 (github.com/charonlabs/mail)",
"Authorization": f"Bearer {MAIL_TOKEN}",
Expand Down
6 changes: 3 additions & 3 deletions src/mail/protocol/src/mail_protocol/network/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ class AdminListPostRequest(BaseModel):

class AdminListPatchRequest(BaseModel):
"""
Corresponds to `PATCH /admin/lists/{list_address}`.
Corresponds to `PATCH /admin/lists/{local_address}`.

All fields are optional; only the policy is mutable at v1. The
canonical address (name, swarm, host) is immutable for the life of
Expand All @@ -225,8 +225,8 @@ class AdminListPatchRequest(BaseModel):

class ListMemberPostRequest(BaseModel):
"""
Corresponds to ``POST /lists/{list_address}/members``
and ``POST /admin/lists/{list_address}/members``.
Corresponds to ``POST /lists/{local_address}/subscribe``
and ``POST /admin/lists/{local_address}/members``.

``member_address`` is the address being added. For the public
subscribe path, this must match the authenticated bearer
Expand Down
22 changes: 11 additions & 11 deletions src/mail/protocol/src/mail_protocol/network/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ class AdminAgentsGetResponse(BaseModel):

class AdminAgentGetResponse(BaseModel):
"""
Corresponds to `GET /admin/agents/{agent_address}`.
Corresponds to `GET /admin/agents/{local_address}`.
Contains a specific MAIL agent registered on this server.
"""

Expand All @@ -334,7 +334,7 @@ class AdminAgentGetResponse(BaseModel):

class AdminAgentPostResponse(BaseModel):
"""
Corresponds to `POST /admin/agent`.
Corresponds to `POST /admin/agents`.
Contains the new MAIL agent registered on this server.
"""

Expand All @@ -344,7 +344,7 @@ class AdminAgentPostResponse(BaseModel):

class AdminAgentDeleteResponse(BaseModel):
"""
Corresponds to `DELETE /admin/agent/{agent_address}`.
Corresponds to `DELETE /admin/agents/{local_address}`.
Contains a newly-deleted MAIL agent registered on this server.
"""

Expand Down Expand Up @@ -516,7 +516,7 @@ class AdminListsGetResponse(BaseModel):

class AdminListGetResponse(BaseModel):
"""
Corresponds to `GET /admin/lists/{list_address}`.
Corresponds to `GET /admin/lists/{local_address}`.
"""

mail_list: MAILListInBackend
Expand All @@ -534,7 +534,7 @@ class AdminListPostResponse(BaseModel):

class AdminListPatchResponse(BaseModel):
"""
Corresponds to `PATCH /admin/lists/{list_address}`.
Corresponds to `PATCH /admin/lists/{local_address}`.
"""

mail_list: MAILListInBackend
Expand All @@ -543,7 +543,7 @@ class AdminListPatchResponse(BaseModel):

class AdminListDeleteResponse(BaseModel):
"""
Corresponds to `DELETE /admin/lists/{list_address}`.
Corresponds to `DELETE /admin/lists/{local_address}`.
"""

mail_list: MAILListInBackend
Expand All @@ -563,7 +563,7 @@ class ListsGetResponse(BaseModel):

class ListGetResponse(BaseModel):
"""
Corresponds to `GET /lists/{list_address}`.
Corresponds to `GET /lists/{local_address}`.
"""

mail_list: MAILListInBackend
Expand All @@ -572,8 +572,8 @@ class ListGetResponse(BaseModel):

class ListMemberPostResponse(BaseModel):
"""
Corresponds to `POST /lists/{list_address}/subscribe` and
`POST /admin/lists/{list_address}/members`. The updated list with
Corresponds to `POST /lists/{local_address}/subscribe` and
`POST /admin/lists/{local_address}/members`. The updated list with
the member appended (idempotent — re-adding an existing member is
a no-op).
"""
Expand All @@ -584,8 +584,8 @@ class ListMemberPostResponse(BaseModel):

class ListMemberDeleteResponse(BaseModel):
"""
Corresponds to `POST /lists/{list_address}/unsubscribe`
and `DELETE /lists/{list_address}/members/{member_address}`.
Corresponds to `POST /lists/{local_address}/unsubscribe`
and `DELETE /admin/lists/{local_address}/members/{member_address}`.
The updated list with the member removed (idempotent — removing a
non-member is a no-op).
"""
Expand Down
9 changes: 7 additions & 2 deletions src/mail/server/src/mail_server/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ class MAILServerBackend(Protocol):
A generic base class for the MAIL server backend.
"""

# The host this server answers for (e.g. ``mail.example.com``). Set
# during ``on_server_startup``. Routers read this to reconstruct a
# full canonical address from a local path-param identifier.
host: str

#
# Lifecyle handlers
#
Expand Down Expand Up @@ -378,7 +383,7 @@ async def admin_get_agents(
async def admin_get_agent(
self,
admin: MAILAdmin,
agent_address: str,
local_address: str,
) -> MAILAgent:
"""
Get a specific registered agent by local address (agent@swarm).
Expand All @@ -402,7 +407,7 @@ async def admin_post_agent(
async def admin_delete_agent(
self,
admin: MAILAdmin,
agent_address: str,
local_address: str,
) -> MAILAgent:
"""
Delete an existing MAIL agent by local address (agent@swarm).
Expand Down
18 changes: 9 additions & 9 deletions src/mail/server/src/mail_server/backends/memory/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -969,18 +969,18 @@ async def admin_get_agents(
async def admin_get_agent(
self,
admin: MAILAdmin,
agent_address: str,
local_address: str,
) -> MAILAgent:
"""
Get a specific registered agent by local address (agent@swarm).
"""

full_address = f"{agent_address}@{self.host}"
full_address = f"{local_address}@{self.host}"
agent = self.user_agents.get(full_address)
if agent is None:
raise ValueError(f"no agent found with address {agent_address}")
raise ValueError(f"no agent found with address {local_address}")
if agent.user_agent.ua_type != "agent":
raise ValueError(f"invalid agent address: {agent_address}")
raise ValueError(f"invalid agent address: {local_address}")

return agent.user_agent

Expand Down Expand Up @@ -1022,25 +1022,25 @@ async def admin_post_agent(
return agent

async def admin_delete_agent(
self, admin: MAILAdmin, agent_address: str
self, admin: MAILAdmin, local_address: str
) -> MAILAgent:
"""
Delete an existing MAIL agent by local address (agent@swarm).
"""

full_address = f"{agent_address}@{self.host}"
full_address = f"{local_address}@{self.host}"
user_agent = self.user_agents.get(full_address)
if user_agent is None:
raise ValueError(f"agent not found: {agent_address}")
raise ValueError(f"agent not found: {local_address}")
if user_agent.user_agent.ua_type != "agent":
raise ValueError(f"invalid agent address: {agent_address}")
raise ValueError(f"invalid agent address: {local_address}")

agent = self.user_agents.pop(full_address)
if not isinstance(agent.user_agent, MAILAgent):
self.user_agents.update(
{full_address: agent}
) # re-add if invalid this far in
raise ValueError(f"invalid agent address: {agent_address}")
raise ValueError(f"invalid agent address: {local_address}")

# remove inbox from self.inboxes
self.inboxes.pop(full_address)
Expand Down
16 changes: 8 additions & 8 deletions src/mail/server/src/mail_server/backends/sqlite/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -776,16 +776,16 @@ async def admin_get_agents(self, admin: MAILAdmin) -> list[str]:
return local_addrs

async def admin_get_agent(
self, admin: MAILAdmin, agent_address: str
self, admin: MAILAdmin, local_address: str
) -> MAILAgent:
full_address = f"{agent_address}@{self.host}"
full_address = f"{local_address}@{self.host}"
async with self._db.session() as session:
agent = await MailStore(session).user_agents.get(full_address)
if agent is None:
raise ValueError(f"no agent found with address {agent_address}")
raise ValueError(f"no agent found with address {local_address}")
inner = agent.user_agent
if not isinstance(inner, MAILAgent):
raise ValueError(f"invalid agent address: {agent_address}")
raise ValueError(f"invalid agent address: {local_address}")
return inner

async def admin_post_agent(
Expand All @@ -811,17 +811,17 @@ async def admin_post_agent(
return agent

async def admin_delete_agent(
self, admin: MAILAdmin, agent_address: str
self, admin: MAILAdmin, local_address: str
) -> MAILAgent:
full_address = f"{agent_address}@{self.host}"
full_address = f"{local_address}@{self.host}"
async with self._db.session() as session:
store = MailStore(session)
agent = await store.user_agents.get(full_address)
if agent is None:
raise ValueError(f"agent not found: {agent_address}")
raise ValueError(f"agent not found: {local_address}")
inner = agent.user_agent
if not isinstance(inner, MAILAgent):
raise ValueError(f"invalid agent address: {agent_address}")
raise ValueError(f"invalid agent address: {local_address}")
await store.user_agents.delete(full_address)
return inner

Expand Down
Loading
Loading