From c2768d3117a97580dec37926bf74b8d631e6db58 Mon Sep 17 00:00:00 2001 From: a2a-platform Date: Thu, 4 Jun 2026 22:28:19 +0000 Subject: [PATCH] deploy --- README.md | 10 + a2a.yaml | 12 + agent.py | 1068 ++++++++++++++++++++ openapi.json | 2494 ++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 7 + 5 files changed, 3591 insertions(+) create mode 100644 README.md create mode 100644 a2a.yaml create mode 100644 agent.py create mode 100644 openapi.json create mode 100644 requirements.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..5f1f52e --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# mailu-openapi-agent + +Generated A2APack agent for Mailu API. + +- Source OpenAPI: https://mail.a2acloud.io/api/v1/swagger.json +- Generated operations: 38 +- Main skill: `auto` + +The generated code is intentionally editable. Tune prompts, operation +grouping, auth names, and safety policy before publishing serious agents. diff --git a/a2a.yaml b/a2a.yaml new file mode 100644 index 0000000..bfb1c65 --- /dev/null +++ b/a2a.yaml @@ -0,0 +1,12 @@ +name: mailu-openapi-agent +version: '1.0' +entrypoint: agent:MailuOpenapiAgent +description: Mailu administration agent generated from the live Mailu OpenAPI spec. +runtime: + resources: + cpu: 200m + memory: 512Mi + egress: + allow_hosts: + - mail.a2acloud.io + deny_internet_by_default: true diff --git a/agent.py b/agent.py new file mode 100644 index 0000000..1933fac --- /dev/null +++ b/agent.py @@ -0,0 +1,1068 @@ +from __future__ import annotations + +import base64 +import json +import re +from typing import Any + +import httpx +from deepagents import create_deep_agent +from langchain_core.messages import BaseMessage +from langchain_core.tools import StructuredTool +from langchain_openai import ChatOpenAI +from pydantic import BaseModel, Field + +from a2a_pack import ( + A2AAgent, + ConsumerSetup, + ConsumerSetupField, + ConsumerSetupMissing, + EgressPolicy, + LLMProvisioning, + Pricing, + Resources, + RunContext, + skill, +) + + +DEFAULT_BASE_URL = "https://mail.a2acloud.io/api/v1" +OPERATIONS = json.loads("{\n \"create_alias\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new alias\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_alias\",\n \"parameters\": [],\n \"path\": \"/alias\",\n \"request_body\": {\n \"$ref\": \"#/definitions/Alias\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_alias\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new alias\",\n \"tags\": [\n \"alias\"\n ]\n },\n \"create_alternative\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new alternative (for domain)\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_alternative\",\n \"parameters\": [],\n \"path\": \"/alternative\",\n \"request_body\": {\n \"$ref\": \"#/definitions/AlternativeDomain\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_alternative\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new alternative (for domain)\",\n \"tags\": [\n \"alternative\"\n ]\n },\n \"create_domain\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new domain\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_domain\",\n \"parameters\": [],\n \"path\": \"/domain\",\n \"request_body\": {\n \"$ref\": \"#/definitions/Domain\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_domain\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"create_manager\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new domain manager for the specified domain\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_manager\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}/manager\",\n \"request_body\": {\n \"$ref\": \"#/definitions/ManagerCreate\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_manager\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new domain manager for the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"create_relay\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new relay\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_relay\",\n \"parameters\": [],\n \"path\": \"/relay\",\n \"request_body\": {\n \"$ref\": \"#/definitions/Relay\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_relay\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new relay\",\n \"tags\": [\n \"relay\"\n ]\n },\n \"create_token\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new token\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_token\",\n \"parameters\": [],\n \"path\": \"/token\",\n \"request_body\": {\n \"$ref\": \"#/definitions/TokenPost\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_token\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new token\",\n \"tags\": [\n \"token\"\n ]\n },\n \"create_token_2\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new token for the specified user\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_token_2\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"email\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/tokenuser/{email}\",\n \"request_body\": {\n \"$ref\": \"#/definitions/TokenPost2\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_token_2\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new token for the specified user\",\n \"tags\": [\n \"token\"\n ]\n },\n \"create_user\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new user\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_user\",\n \"parameters\": [],\n \"path\": \"/user\",\n \"request_body\": {\n \"$ref\": \"#/definitions/UserCreate\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_user\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new user\",\n \"tags\": [\n \"user\"\n ]\n },\n \"delete_alias\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete the specified alias\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_alias\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"alias\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/alias/{alias}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"delete_alias\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Delete the specified alias\",\n \"tags\": [\n \"alias\"\n ]\n },\n \"delete_alternative\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete the specified alternative (for domain)\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_alternative\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"alt\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/alternative/{alt}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"delete_alternative\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Delete the specified alternative (for domain)\",\n \"tags\": [\n \"alternative\"\n ]\n },\n \"delete_domain\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete the specified domain\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_domain\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"delete_domain\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Delete the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"delete_manager\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete the specified manager of the specified domain\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_manager\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"email\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}/manager/{email}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"delete_manager\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Delete the specified manager of the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"delete_relay\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete the specified relay\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_relay\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"name\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/relay/{name}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"delete_relay\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Delete the specified relay\",\n \"tags\": [\n \"relay\"\n ]\n },\n \"delete_token\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete the specified token\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_token\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"token_id\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/token/{token_id}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"delete_token\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Delete the specified token\",\n \"tags\": [\n \"token\"\n ]\n },\n \"delete_user\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete the specified user\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_user\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"email\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/user/{email}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"delete_user\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Delete the specified user\",\n \"tags\": [\n \"user\"\n ]\n },\n \"find_alias\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Look up the specified alias\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_alias\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"alias\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/alias/{alias}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_alias\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Look up the specified alias\",\n \"tags\": [\n \"alias\"\n ]\n },\n \"find_alias_filter_domain\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Look up the aliases of the specified domain\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_alias_filter_domain\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/alias/destination/{domain}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_alias_filter_domain\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Look up the aliases of the specified domain\",\n \"tags\": [\n \"alias\"\n ]\n },\n \"find_alternative\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Look up the specified alternative (of domain)\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_alternative\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"alt\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/alternative/{alt}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_alternative\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Look up the specified alternative (of domain)\",\n \"tags\": [\n \"alternative\"\n ]\n },\n \"find_domain\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Look up the specified domain\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_domain\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_domain\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Look up the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"find_manager\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Check if the specified user is a manager of the specified domain\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_manager\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"email\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}/manager/{email}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_manager\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Check if the specified user is a manager of the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"find_relay\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Look up the specified relay\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_relay\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"name\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/relay/{name}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_relay\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Look up the specified relay\",\n \"tags\": [\n \"relay\"\n ]\n },\n \"find_token\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Find the specified token\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_token\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"token_id\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/token/{token_id}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_token\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Find the specified token\",\n \"tags\": [\n \"token\"\n ]\n },\n \"find_tokens_of_user\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Look up all the tokens of the specified user\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_tokens_of_user\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"email\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/tokenuser/{email}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_tokens_of_user\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Look up all the tokens of the specified user\",\n \"tags\": [\n \"token\"\n ]\n },\n \"find_user\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Look up the specified user\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_user\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"email\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/user/{email}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_user\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Look up the specified user\",\n \"tags\": [\n \"user\"\n ]\n },\n \"generate_dkim\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Generate new DKIM/DMARC keys for the specified domain\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"generate_dkim\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}/dkim\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"generate_dkim\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Generate new DKIM/DMARC keys for the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"list_alias\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all aliases\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_alias\",\n \"parameters\": [],\n \"path\": \"/alias\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_alias\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all aliases\",\n \"tags\": [\n \"alias\"\n ]\n },\n \"list_alternative\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all alternatives\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_alternative\",\n \"parameters\": [],\n \"path\": \"/alternative\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_alternative\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all alternatives\",\n \"tags\": [\n \"alternative\"\n ]\n },\n \"list_domain\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all domains\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_domain\",\n \"parameters\": [],\n \"path\": \"/domain\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_domain\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all domains\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"list_managers\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all managers of the specified domain\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_managers\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}/manager\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_managers\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all managers of the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"list_relays\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all relays\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_relays\",\n \"parameters\": [],\n \"path\": \"/relay\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_relays\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all relays\",\n \"tags\": [\n \"relay\"\n ]\n },\n \"list_tokens\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all tokens\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_tokens\",\n \"parameters\": [],\n \"path\": \"/token\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_tokens\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all tokens\",\n \"tags\": [\n \"token\"\n ]\n },\n \"list_user\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all users\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_user\",\n \"parameters\": [],\n \"path\": \"/user\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_user\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all users\",\n \"tags\": [\n \"user\"\n ]\n },\n \"list_user_domain\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all the users from the specified domain\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_user_domain\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}/users\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_user_domain\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all the users from the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"update_alias\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Update the specfied alias\",\n \"destructive\": true,\n \"method\": \"PATCH\",\n \"operation_id\": \"update_alias\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"alias\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/alias/{alias}\",\n \"request_body\": {\n \"$ref\": \"#/definitions/AliasUpdate\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"update_alias\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Update the specfied alias\",\n \"tags\": [\n \"alias\"\n ]\n },\n \"update_domain\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Update the specified domain\",\n \"destructive\": true,\n \"method\": \"PATCH\",\n \"operation_id\": \"update_domain\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}\",\n \"request_body\": {\n \"$ref\": \"#/definitions/DomainUpdate\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"update_domain\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Update the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"update_relay\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Update the specified relay\",\n \"destructive\": true,\n \"method\": \"PATCH\",\n \"operation_id\": \"update_relay\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"name\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/relay/{name}\",\n \"request_body\": {\n \"$ref\": \"#/definitions/RelayUpdate\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"update_relay\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Update the specified relay\",\n \"tags\": [\n \"relay\"\n ]\n },\n \"update_token\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Update the specified token\",\n \"destructive\": true,\n \"method\": \"PATCH\",\n \"operation_id\": \"update_token\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"token_id\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/token/{token_id}\",\n \"request_body\": {\n \"$ref\": \"#/definitions/TokenPost2\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"update_token\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Update the specified token\",\n \"tags\": [\n \"token\"\n ]\n },\n \"update_user\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Update the specified user\",\n \"destructive\": true,\n \"method\": \"PATCH\",\n \"operation_id\": \"update_user\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"email\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/user/{email}\",\n \"request_body\": {\n \"$ref\": \"#/definitions/UserUpdate\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"update_user\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Update the specified user\",\n \"tags\": [\n \"user\"\n ]\n }\n}") +ROOT_SECURITY = json.loads("[\n {\n \"Bearer\": []\n }\n]") +SECURITY_SCHEMES = json.loads("{\n \"Bearer\": {\n \"in\": \"header\",\n \"name\": \"Authorization\",\n \"type\": \"apiKey\"\n }\n}") +SECURITY_FIELDS = json.loads("{\n \"Bearer\": {\n \"field\": \"AUTHORIZATION\",\n \"kind\": \"apiKey\",\n \"location\": \"header\",\n \"name\": \"Authorization\"\n }\n}") +PATH_PARAMETER_RE = re.compile(r"{([^}/]+)}") + + +class OperationInput(BaseModel): + parameters: dict[str, Any] = Field( + default_factory=dict, + description="Path, query, header, and cookie parameters keyed by OpenAPI parameter name.", + ) + body: Any | None = Field(default=None, description="JSON request body, when the operation accepts one.") + + +class MailuOpenapiAgent(A2AAgent): + name = "mailu-openapi-agent" + description = "Mailu administration agent generated from the live Mailu OpenAPI spec." + version = "1.0" + consumer_setup = ConsumerSetup.from_fields( + ConsumerSetupField.config("OPENAPI_BASE_URL", label="API base URL", description="Override the default API server (https://mail.a2acloud.io/api/v1).", required=False, input_type="url"), + ConsumerSetupField.secret("AUTHORIZATION", label="Bearer API key", description="Sent as header parameter 'Authorization'.", required=True), + ) + llm_provisioning = LLMProvisioning.PLATFORM + pricing = Pricing( + price_per_call_usd=0.0, + caller_pays_llm=False, + notes="Uses a scoped platform LLM grant through ctx.llm.", + ) + resources = Resources(cpu="200m", memory="512Mi") + egress = EgressPolicy( + allow_hosts=('mail.a2acloud.io',), + deny_internet_by_default=True, + ) + tools_used = ("openapi", "deepagents") + capabilities = { + "openapi_auto_agent": { + "operation_count": len(OPERATIONS), + "default_base_url": DEFAULT_BASE_URL, + "source_openapi_url": 'https://mail.a2acloud.io/api/v1/swagger.json', + "source_openapi_urls": ['https://mail.a2acloud.io/api/v1/swagger.json'], + "server_urls": ['https://mail.a2acloud.io/api/v1'], + "regenerable": True, + "security_schemes": list(SECURITY_SCHEMES), + } + } + + @skill( + name="auto", + description="Use the OpenAPI service to complete a natural-language goal.", + tags=("openapi", "auto"), + timeout_seconds=180, + ) + async def auto(self, ctx: RunContext, goal: str) -> dict[str, Any]: + creds = ctx.llm + if not creds.api_key: + return { + "error": "llm_credentials_missing", + "final": ( + "LLM credentials were not available for this run. Hosted " + "generated OpenAPI agents should receive a platform LLM grant." + ), + "messages": [], + } + model_kwargs: dict[str, Any] = { + "model": creds.model, + "base_url": creds.base_url, + "api_key": creds.api_key, + } + if creds.temperature_mode != "omit" and creds.temperature is not None: + model_kwargs["temperature"] = creds.temperature + if creds.extra_body: + model_kwargs["extra_body"] = dict(creds.extra_body) + model = ChatOpenAI(**model_kwargs) + graph = create_deep_agent( + model=model, + tools=self._operation_tools(ctx), + system_prompt=self._system_prompt(), + ) + result = await graph.ainvoke({"messages": [{"role": "user", "content": goal}]}) + messages = result.get("messages", []) if isinstance(result, dict) else [] + final = _message_text(messages[-1]) if messages else result + return { + "final": final, + "messages": [_message_to_dict(message) for message in messages[-8:]], + } + + @skill( + name="create_alias", + description="POST /alias - Create a new alias", + tags=('alias',), + timeout_seconds=120, + ) + async def create_alias( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "create_alias", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="list_alias", + description="GET /alias - List all aliases", + tags=('alias',), + timeout_seconds=60, + ) + async def list_alias( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "list_alias", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="find_alias_filter_domain", + description="GET /alias/destination/{domain} - Look up the aliases of the specified domain", + tags=('alias',), + timeout_seconds=60, + ) + async def find_alias_filter_domain( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "find_alias_filter_domain", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="update_alias", + description="PATCH /alias/{alias} - Update the specfied alias", + tags=('alias',), + timeout_seconds=120, + ) + async def update_alias( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "update_alias", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="delete_alias", + description="DELETE /alias/{alias} - Delete the specified alias", + tags=('alias',), + timeout_seconds=120, + ) + async def delete_alias( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "delete_alias", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="find_alias", + description="GET /alias/{alias} - Look up the specified alias", + tags=('alias',), + timeout_seconds=60, + ) + async def find_alias( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "find_alias", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="create_alternative", + description="POST /alternative - Create a new alternative (for domain)", + tags=('alternative',), + timeout_seconds=120, + ) + async def create_alternative( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "create_alternative", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="list_alternative", + description="GET /alternative - List all alternatives", + tags=('alternative',), + timeout_seconds=60, + ) + async def list_alternative( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "list_alternative", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="delete_alternative", + description="DELETE /alternative/{alt} - Delete the specified alternative (for domain)", + tags=('alternative',), + timeout_seconds=120, + ) + async def delete_alternative( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "delete_alternative", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="find_alternative", + description="GET /alternative/{alt} - Look up the specified alternative (of domain)", + tags=('alternative',), + timeout_seconds=60, + ) + async def find_alternative( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "find_alternative", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="create_domain", + description="POST /domain - Create a new domain", + tags=('domain',), + timeout_seconds=120, + ) + async def create_domain( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "create_domain", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="list_domain", + description="GET /domain - List all domains", + tags=('domain',), + timeout_seconds=60, + ) + async def list_domain( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "list_domain", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="update_domain", + description="PATCH /domain/{domain} - Update the specified domain", + tags=('domain',), + timeout_seconds=120, + ) + async def update_domain( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "update_domain", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="delete_domain", + description="DELETE /domain/{domain} - Delete the specified domain", + tags=('domain',), + timeout_seconds=120, + ) + async def delete_domain( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "delete_domain", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="find_domain", + description="GET /domain/{domain} - Look up the specified domain", + tags=('domain',), + timeout_seconds=60, + ) + async def find_domain( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "find_domain", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="generate_dkim", + description="POST /domain/{domain}/dkim - Generate new DKIM/DMARC keys for the specified domain", + tags=('domain',), + timeout_seconds=120, + ) + async def generate_dkim( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "generate_dkim", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="create_manager", + description="POST /domain/{domain}/manager - Create a new domain manager for the specified domain", + tags=('domain',), + timeout_seconds=120, + ) + async def create_manager( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "create_manager", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="list_managers", + description="GET /domain/{domain}/manager - List all managers of the specified domain", + tags=('domain',), + timeout_seconds=60, + ) + async def list_managers( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "list_managers", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="delete_manager", + description="DELETE /domain/{domain}/manager/{email} - Delete the specified manager of the specified domain", + tags=('domain',), + timeout_seconds=120, + ) + async def delete_manager( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "delete_manager", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="find_manager", + description="GET /domain/{domain}/manager/{email} - Check if the specified user is a manager of the specified domain", + tags=('domain',), + timeout_seconds=60, + ) + async def find_manager( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "find_manager", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="list_user_domain", + description="GET /domain/{domain}/users - List all the users from the specified domain", + tags=('domain',), + timeout_seconds=60, + ) + async def list_user_domain( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "list_user_domain", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="create_relay", + description="POST /relay - Create a new relay", + tags=('relay',), + timeout_seconds=120, + ) + async def create_relay( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "create_relay", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="list_relays", + description="GET /relay - List all relays", + tags=('relay',), + timeout_seconds=60, + ) + async def list_relays( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "list_relays", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="update_relay", + description="PATCH /relay/{name} - Update the specified relay", + tags=('relay',), + timeout_seconds=120, + ) + async def update_relay( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "update_relay", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="delete_relay", + description="DELETE /relay/{name} - Delete the specified relay", + tags=('relay',), + timeout_seconds=120, + ) + async def delete_relay( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "delete_relay", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="find_relay", + description="GET /relay/{name} - Look up the specified relay", + tags=('relay',), + timeout_seconds=60, + ) + async def find_relay( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "find_relay", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="create_token", + description="POST /token - Create a new token", + tags=('token',), + timeout_seconds=120, + ) + async def create_token( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "create_token", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="list_tokens", + description="GET /token - List all tokens", + tags=('token',), + timeout_seconds=60, + ) + async def list_tokens( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "list_tokens", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="update_token", + description="PATCH /token/{token_id} - Update the specified token", + tags=('token',), + timeout_seconds=120, + ) + async def update_token( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "update_token", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="delete_token", + description="DELETE /token/{token_id} - Delete the specified token", + tags=('token',), + timeout_seconds=120, + ) + async def delete_token( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "delete_token", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="find_token", + description="GET /token/{token_id} - Find the specified token", + tags=('token',), + timeout_seconds=60, + ) + async def find_token( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "find_token", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="create_token_2", + description="POST /tokenuser/{email} - Create a new token for the specified user", + tags=('token',), + timeout_seconds=120, + ) + async def create_token_2( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "create_token_2", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="find_tokens_of_user", + description="GET /tokenuser/{email} - Look up all the tokens of the specified user", + tags=('token',), + timeout_seconds=60, + ) + async def find_tokens_of_user( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "find_tokens_of_user", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="create_user", + description="POST /user - Create a new user", + tags=('user',), + timeout_seconds=120, + ) + async def create_user( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "create_user", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="list_user", + description="GET /user - List all users", + tags=('user',), + timeout_seconds=60, + ) + async def list_user( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "list_user", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="update_user", + description="PATCH /user/{email} - Update the specified user", + tags=('user',), + timeout_seconds=120, + ) + async def update_user( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "update_user", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="delete_user", + description="DELETE /user/{email} - Delete the specified user", + tags=('user',), + timeout_seconds=120, + ) + async def delete_user( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "delete_user", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="find_user", + description="GET /user/{email} - Look up the specified user", + tags=('user',), + timeout_seconds=60, + ) + async def find_user( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "find_user", + parameters=parameters or {}, + body=body, + ) + + def _operation_tools(self, ctx: RunContext) -> list[StructuredTool]: + tools: list[StructuredTool] = [] + for operation_id, operation in OPERATIONS.items(): + async def call( + parameters: dict[str, Any] | None = None, + body: Any | None = None, + *, + _operation_id: str = operation_id, + ) -> dict[str, Any]: + return await self._request( + ctx, + _operation_id, + parameters=parameters or {}, + body=body, + ) + + tools.append( + StructuredTool.from_function( + coroutine=call, + name=operation["skill_name"], + description=self._tool_description(operation), + args_schema=OperationInput, + ) + ) + return tools + + def _system_prompt(self) -> str: + lines = [ + "You operate an API through generated OpenAPI tools.", + "Call tools to get real results. Do not invent API responses.", + "For write, update, or delete operations, explain the intended action before calling the tool.", + "If an operation reports missing consumer setup, tell the user which setup field is required.", + "If an operation returns 404, 405, 410, or a schema/validation error that suggests the live API no longer matches these tools, tell the user this generated agent may need to be refreshed from the latest OpenAPI spec and ask whether they want to refresh it.", + "", + "Available operations:", + ] + for operation in OPERATIONS.values(): + marker = "WRITE" if operation.get("destructive") else "READ" + lines.append( + f"- {operation['skill_name']}: {marker} {operation['method']} {operation['path']} — {operation['summary']}" + ) + return "\n".join(lines) + + def _tool_description(self, operation: dict[str, Any]) -> str: + parts = [ + f"{operation['method']} {operation['path']}", + str(operation.get("description") or operation.get("summary") or ""), + ] + if operation.get("parameters"): + parts.append( + "Parameters: " + + ", ".join( + f"{p['name']} in {p['in']}{' required' if p.get('required') else ''}" + for p in operation["parameters"] + ) + ) + if operation.get("request_body") is not None: + parts.append("Accepts a JSON request body.") + return "\n".join(part for part in parts if part) + + async def _request( + self, + ctx: RunContext, + operation_id: str, + *, + parameters: dict[str, Any], + body: Any | None, + ) -> dict[str, Any]: + operation = OPERATIONS[operation_id] + default_base_url = str(operation.get("base_url") or DEFAULT_BASE_URL).rstrip("/") + base_url_field = str(operation.get("base_url_field") or "OPENAPI_BASE_URL") + base_url = str(ctx.consumer_config(base_url_field, default_base_url) or default_base_url).rstrip("/") + url, query, headers = self._request_parts(ctx, operation, parameters) + request_kwargs: dict[str, Any] = { + "params": query, + "headers": headers, + } + if body is not None: + if isinstance(body, (bytes, bytearray)): + body = body.decode("utf-8", errors="replace") + if isinstance(body, str): + stripped = body.strip() + if stripped and stripped[0] in "{[": + try: + body = json.loads(stripped) + except ValueError: + pass + request_kwargs["json"] = body + async with httpx.AsyncClient(timeout=60.0, follow_redirects=True) as client: + response = await client.request( + operation["method"], + f"{base_url}{url}", + **request_kwargs, + ) + content_type = response.headers.get("content-type", "") + try: + payload: Any = response.json() if "json" in content_type else response.text + except ValueError: + payload = response.text + if response.status_code >= 400: + out = { + "ok": False, + "status_code": response.status_code, + "operation_id": operation_id, + "error": payload, + } + if response.status_code in {404, 405, 410, 422}: + out["stale_openapi_hint"] = ( + "This generated OpenAPI agent may be stale. Ask the user " + "whether they want to refresh it from the latest OpenAPI spec." + ) + return out + return { + "ok": True, + "status_code": response.status_code, + "operation_id": operation_id, + "result": payload, + } + + def _request_parts( + self, + ctx: RunContext, + operation: dict[str, Any], + parameters: dict[str, Any], + ) -> tuple[str, dict[str, Any], dict[str, str]]: + path = operation["path"] + query: dict[str, Any] = {} + headers: dict[str, str] = {} + cookies: dict[str, Any] = {} + for param in operation.get("parameters") or []: + name = param["name"] + if name not in parameters: + if param.get("required"): + raise ValueError(f"missing required parameter {name!r}") + continue + value = parameters[name] + location = param["in"] + if location == "path": + path = path.replace("{" + name + "}", str(value)) + elif location == "query": + query[name] = value + elif location == "header": + headers[name] = str(value) + elif location == "cookie": + cookies[name] = value + unresolved = PATH_PARAMETER_RE.findall(path) + if unresolved: + missing = ", ".join(sorted(set(unresolved))) + raise ValueError("missing required path parameter(s): " + missing) + if cookies: + headers["cookie"] = "; ".join(f"{key}={value}" for key, value in cookies.items()) + auth_headers, auth_query = self._auth_for_operation(ctx, operation) + headers.update(auth_headers) + query.update(auth_query) + return path, query, headers + + def _auth_for_operation(self, ctx: RunContext, operation: dict[str, Any]) -> tuple[dict[str, str], dict[str, Any]]: + requirements = operation.get("security") + if requirements is None: + requirements = ROOT_SECURITY + if requirements == []: + return {}, {} + missing: list[str] = [] + for requirement in requirements or []: + if not isinstance(requirement, dict) or not requirement: + return {}, {} + headers: dict[str, str] = {} + query: dict[str, Any] = {} + ok = True + for scheme_name in requirement: + mapping = SECURITY_FIELDS.get(scheme_name) or {} + secret_name = mapping.get("field") + value = _consumer_secret_optional(ctx, secret_name) if secret_name else "" + if not value: + ok = False + if secret_name: + missing.append(secret_name) + continue + kind = mapping.get("kind") + if kind == "apiKey": + prefix = mapping.get("prefix") or "" + sent_value = f"{prefix}{value}" + if mapping.get("location") == "query": + query[mapping.get("name") or scheme_name] = sent_value + else: + headers[mapping.get("name") or scheme_name] = sent_value + elif kind in {"http", "oauth2"}: + scheme = mapping.get("scheme") or "Bearer" + if scheme.lower() == "basic": + token = base64.b64encode(value.encode("utf-8")).decode("ascii") + headers["authorization"] = f"Basic {token}" + else: + headers["authorization"] = f"{scheme} {value}" + else: + ok = False + if ok: + return headers, query + if missing: + raise ConsumerSetupMissing( + "operation requires consumer setup secret(s): " + + ", ".join(sorted(set(missing))) + ) + return {}, {} + + +def _consumer_secret_optional(ctx: RunContext, name: str | None) -> str: + if not name: + return "" + try: + return ctx.consumer_secret(name) + except ConsumerSetupMissing: + return "" + + +def _message_text(message: Any) -> Any: + content = getattr(message, "content", message) + return content + + +def _message_to_dict(message: Any) -> dict[str, Any]: + if isinstance(message, BaseMessage): + return message.model_dump(mode="json") + if hasattr(message, "model_dump"): + return message.model_dump(mode="json") + if isinstance(message, dict): + return message + return {"content": str(message)} + + +agent = MailuOpenapiAgent() diff --git a/openapi.json b/openapi.json new file mode 100644 index 0000000..9633f1b --- /dev/null +++ b/openapi.json @@ -0,0 +1,2494 @@ +{ + "basePath": "/api/v1", + "consumes": [ + "application/json" + ], + "definitions": { + "Alias": { + "allOf": [ + { + "$ref": "#/definitions/AliasUpdate" + }, + { + "properties": { + "destination": { + "items": { + "description": "destination email address", + "example": "user@example.com", + "type": "string" + }, + "type": "array" + }, + "email": { + "description": "the alias email address", + "example": "user@example.com", + "type": "string" + } + }, + "required": [ + "email" + ], + "type": "object" + } + ] + }, + "AliasUpdate": { + "properties": { + "comment": { + "description": "a comment", + "type": "string" + }, + "destination": { + "items": { + "description": "alias email address", + "example": "user@example.com", + "type": "string" + }, + "type": "array" + }, + "wildcard": { + "description": "enable SQL Like wildcard syntax", + "type": "boolean" + } + }, + "type": "object" + }, + "AlternativeDomain": { + "properties": { + "domain": { + "description": "domain FQDN", + "example": "example.com", + "type": "string" + }, + "name": { + "description": "alternative FQDN", + "example": "example2.com", + "type": "string" + } + }, + "required": [ + "domain", + "name" + ], + "type": "object" + }, + "Domain": { + "properties": { + "alternatives": { + "example": "[\"example.com\"]", + "items": { + "description": "FQDN", + "type": "string" + }, + "type": "array" + }, + "comment": { + "description": "a comment", + "type": "string" + }, + "max_aliases": { + "default": -1, + "description": "maximum number of aliases", + "minimum": -1, + "type": "integer" + }, + "max_quota_bytes": { + "description": "maximum quota for mailbox", + "minimum": 0, + "type": "integer" + }, + "max_users": { + "default": -1, + "description": "maximum number of users", + "minimum": -1, + "type": "integer" + }, + "name": { + "description": "FQDN (e.g. example.com)", + "example": "example.com", + "type": "string" + }, + "signup_enabled": { + "description": "allow signup", + "type": "boolean" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "DomainGet": { + "properties": { + "alternatives": { + "example": "[\"example.com\"]", + "items": { + "description": "FQDN", + "type": "string" + }, + "type": "array" + }, + "comment": { + "description": "a comment", + "type": "string" + }, + "dns_autoconfig": { + "items": { + "description": "DNS client auto-configuration entry", + "type": "string" + }, + "type": "array" + }, + "dns_dkim": { + "type": "string" + }, + "dns_dmarc": { + "type": "string" + }, + "dns_dmarc_report": { + "type": "string" + }, + "dns_mx": { + "type": "string" + }, + "dns_spf": { + "type": "string" + }, + "dns_tlsa": { + "items": { + "type": "string" + }, + "type": "array" + }, + "managers": { + "items": { + "description": "manager of domain", + "type": "string" + }, + "type": "array" + }, + "max_aliases": { + "default": -1, + "description": "maximum number of aliases", + "minimum": -1, + "type": "integer" + }, + "max_quota_bytes": { + "description": "maximum quota for mailbox", + "minimum": 0, + "type": "integer" + }, + "max_users": { + "default": -1, + "description": "maximum number of users", + "minimum": -1, + "type": "integer" + }, + "name": { + "description": "FQDN (e.g. example.com)", + "example": "example.com", + "type": "string" + }, + "signup_enabled": { + "description": "allow signup", + "type": "boolean" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "DomainUpdate": { + "properties": { + "alternatives": { + "example": "[\"example.com\"]", + "items": { + "description": "FQDN", + "type": "string" + }, + "type": "array" + }, + "comment": { + "description": "a comment", + "type": "string" + }, + "max_aliases": { + "default": -1, + "description": "maximum number of aliases", + "minimum": -1, + "type": "integer" + }, + "max_quota_bytes": { + "description": "maximum quota for mailbox", + "minimum": 0, + "type": "integer" + }, + "max_users": { + "default": -1, + "description": "maximum number of users", + "minimum": -1, + "type": "integer" + }, + "signup_enabled": { + "description": "allow signup", + "type": "boolean" + } + }, + "type": "object" + }, + "Manager": { + "properties": { + "managers": { + "items": { + "description": "manager of domain", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "ManagerCreate": { + "properties": { + "user_email": { + "description": "email address of manager", + "type": "string" + } + }, + "required": [ + "user_email" + ], + "type": "object" + }, + "Relay": { + "properties": { + "comment": { + "description": "a comment", + "type": "string" + }, + "name": { + "description": "relayed domain name", + "example": "example.com", + "type": "string" + }, + "smtp": { + "description": "remote host", + "example": "example.com", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "RelayUpdate": { + "properties": { + "comment": { + "description": "a comment", + "type": "string" + }, + "smtp": { + "description": "remote host", + "example": "example.com", + "type": "string" + } + }, + "type": "object" + }, + "Response": { + "properties": { + "code": { + "type": "integer" + }, + "message": { + "type": "string" + } + }, + "type": "object" + }, + "TokenGetResponse": { + "properties": { + "AuthorizedIP": { + "items": { + "description": "White listed IP addresses or networks that may use this token.", + "example": "203.0.113.0/24", + "type": "string" + }, + "type": "array" + }, + "Created": { + "description": "The date when the token was created", + "example": "John.Doe@example.com", + "type": "string" + }, + "Last edit": { + "description": "The date when the token was last modifified", + "example": "John.Doe@example.com", + "type": "string" + }, + "comment": { + "description": "A description for the token. This description is shown on the Authentication tokens page", + "example": "my comment", + "type": "string" + }, + "email": { + "description": "The email address of the user", + "example": "John.Doe@example.com", + "type": "string" + }, + "id": { + "description": "The record id of the token (unique identifier)", + "example": "1", + "type": "string" + } + }, + "type": "object" + }, + "TokenPost": { + "properties": { + "AuthorizedIP": { + "items": { + "description": "White listed IP addresses or networks that may use this token.", + "example": "203.0.113.0/24", + "type": "string" + }, + "type": "array" + }, + "comment": { + "description": "A description for the token. This description is shown on the Authentication tokens page", + "example": "my comment", + "type": "string" + }, + "email": { + "description": "The email address of the user", + "example": "John.Doe@example.com", + "type": "string" + } + }, + "required": [ + "email" + ], + "type": "object" + }, + "TokenPost2": { + "properties": { + "AuthorizedIP": { + "items": { + "description": "White listed IP addresses or networks that may use this token.", + "example": "203.0.113.0/24", + "type": "string" + }, + "type": "array" + }, + "comment": { + "description": "A description for the token. This description is shown on the Authentication tokens page", + "example": "my comment", + "type": "string" + } + }, + "type": "object" + }, + "TokenPostResponse": { + "properties": { + "AuthorizedIP": { + "items": { + "description": "White listed IP addresses or networks that may use this token.", + "example": "203.0.113.0/24", + "type": "string" + }, + "type": "array" + }, + "Created": { + "description": "The date when the token was created", + "example": "John.Doe@example.com", + "type": "string" + }, + "comment": { + "description": "A description for the token. This description is shown on the Authentication tokens page", + "example": "my comment", + "type": "string" + }, + "email": { + "description": "The email address of the user", + "example": "John.Doe@example.com", + "type": "string" + }, + "id": { + "description": "The record id of the token (unique identifier)", + "example": "1", + "type": "string" + }, + "token": { + "description": "The created authentication token for the user.", + "example": "2caf6607de5129e4748a2c061aee56f2", + "type": "string" + } + }, + "type": "object" + }, + "UserCreate": { + "properties": { + "allow_spoofing": { + "description": "Allow the user to spoof the sender (send email as anyone)", + "type": "boolean" + }, + "change_pw_next_login": { + "description": "Force the user to change their password at next login", + "type": "boolean" + }, + "comment": { + "description": "A description for the user. This description is shown on the Users page", + "example": "my comment", + "type": "string" + }, + "displayed_name": { + "description": "The display name of the user within the Admin GUI", + "example": "John Doe", + "type": "string" + }, + "email": { + "description": "The email address of the user", + "example": "John.Doe@example.com", + "type": "string" + }, + "enable_imap": { + "description": "Allow email retrieval via IMAP", + "type": "boolean" + }, + "enable_pop": { + "description": "Allow email retrieval via POP3", + "type": "boolean" + }, + "enabled": { + "description": "Enable the user. When an user is disabled, the user is unable to login to the Admin GUI or webmail or access his email via IMAP/POP3 or send mail", + "type": "boolean" + }, + "forward_destination": { + "example": "[\"Other@example.com\"]", + "items": { + "description": "Email address to forward emails to", + "type": "string" + }, + "type": "array" + }, + "forward_enabled": { + "description": "Enable auto forwarding", + "type": "boolean" + }, + "forward_keep": { + "description": "Keep a copy of the forwarded email in the inbox", + "type": "boolean" + }, + "global_admin": { + "description": "Make the user a global administrator", + "type": "boolean" + }, + "quota_bytes": { + "description": "The maximum quota for the user\u2019s email box in bytes", + "example": "1000000000", + "type": "integer" + }, + "raw_password": { + "description": "The raw (plain text) password of the user. Mailu will hash the password using BCRYPT-SHA256", + "example": "secret", + "type": "string" + }, + "reply_body": { + "description": "The body of the automatic reply email", + "example": "Hello, I am out of office. I will respond when I am back.", + "type": "string" + }, + "reply_enabled": { + "description": "Enable automatic replies. This is also known as out of office (ooo) or out of facility (oof) replies", + "type": "boolean" + }, + "reply_enddate": { + "description": "End date for automatic replies in YYYY-MM-DD format.", + "example": "2022-02-22", + "format": "date", + "type": "string" + }, + "reply_startdate": { + "description": "Start date for automatic replies in YYYY-MM-DD format.", + "example": "2022-02-10", + "format": "date", + "type": "string" + }, + "reply_subject": { + "description": "Optional subject for the automatic reply", + "example": "Out of office", + "type": "string" + }, + "spam_enabled": { + "description": "Enable the spam filter", + "type": "boolean" + }, + "spam_mark_as_read": { + "description": "Enable marking spam mails as read", + "type": "boolean" + }, + "spam_threshold": { + "description": "The user defined spam filter tolerance", + "example": "80", + "type": "integer" + } + }, + "required": [ + "email", + "raw_password" + ], + "type": "object" + }, + "UserGet": { + "properties": { + "allow_spoofing": { + "description": "Allow the user to spoof the sender (send email as anyone)", + "type": "boolean" + }, + "change_pw_next_login": { + "description": "Force the user to change their password at next login", + "type": "boolean" + }, + "comment": { + "description": "A description for the user. This description is shown on the Users page", + "example": "my comment", + "type": "string" + }, + "displayed_name": { + "description": "The display name of the user within the Admin GUI", + "example": "John Doe", + "type": "string" + }, + "email": { + "description": "The email address of the user", + "example": "John.Doe@example.com", + "type": "string" + }, + "enable_imap": { + "description": "Allow email retrieval via IMAP", + "type": "boolean" + }, + "enable_pop": { + "description": "Allow email retrieval via POP3", + "type": "boolean" + }, + "enabled": { + "description": "Enable the user. When an user is disabled, the user is unable to login to the Admin GUI or webmail or access his email via IMAP/POP3 or send mail", + "type": "boolean" + }, + "forward_destination": { + "example": "[\"Other@example.com\"]", + "items": { + "description": "Email address to forward emails to", + "type": "string" + }, + "type": "array" + }, + "forward_enabled": { + "description": "Enable auto forwarding", + "type": "boolean" + }, + "forward_keep": { + "description": "Keep a copy of the forwarded email in the inbox", + "type": "boolean" + }, + "global_admin": { + "description": "Make the user a global administrator", + "type": "boolean" + }, + "password": { + "description": "Hash of the user's password; Example='$bcrypt-sha256$v=2,t=2b,r=12$fmsAdJbYAD1gGQIE5nfJq.$zLkQUEs2XZfTpAEpcix/1k5UTNPm0jO'", + "type": "string" + }, + "quota_bytes": { + "description": "The maximum quota for the user\u2019s email box in bytes", + "example": "1000000000", + "type": "integer" + }, + "quota_bytes_used": { + "description": "The size of the user\u2019s email box in bytes", + "example": "5000000", + "type": "integer" + }, + "reply_body": { + "description": "The body of the automatic reply email", + "example": "Hello, I am out of office. I will respond when I am back.", + "type": "string" + }, + "reply_enabled": { + "description": "Enable automatic replies. This is also known as out of office (ooo) or out of facility (oof) replies", + "type": "boolean" + }, + "reply_enddate": { + "description": "End date for automatic replies in YYYY-MM-DD format.", + "example": "2022-02-22", + "format": "date", + "type": "string" + }, + "reply_startdate": { + "description": "Start date for automatic replies in YYYY-MM-DD format.", + "example": "2022-02-10", + "format": "date", + "type": "string" + }, + "reply_subject": { + "description": "Optional subject for the automatic reply", + "example": "Out of office", + "type": "string" + }, + "spam_enabled": { + "description": "Enable the spam filter", + "type": "boolean" + }, + "spam_mark_as_read": { + "description": "Enable marking spam mails as read", + "type": "boolean" + }, + "spam_threshold": { + "description": "The user defined spam filter tolerance", + "example": "80", + "type": "integer" + } + }, + "type": "object" + }, + "UserUpdate": { + "properties": { + "allow_spoofing": { + "description": "Allow the user to spoof the sender (send email as anyone)", + "type": "boolean" + }, + "change_pw_next_login": { + "description": "Force the user to change their password at next login", + "type": "boolean" + }, + "comment": { + "description": "A description for the user. This description is shown on the Users page", + "example": "my comment", + "type": "string" + }, + "displayed_name": { + "description": "The display name of the user within the Admin GUI", + "example": "John Doe", + "type": "string" + }, + "enable_imap": { + "description": "Allow email retrieval via IMAP", + "type": "boolean" + }, + "enable_pop": { + "description": "Allow email retrieval via POP3", + "type": "boolean" + }, + "enabled": { + "description": "Enable the user. When an user is disabled, the user is unable to login to the Admin GUI or webmail or access his email via IMAP/POP3 or send mail", + "type": "boolean" + }, + "forward_destination": { + "example": "[\"Other@example.com\"]", + "items": { + "description": "Email address to forward emails to", + "type": "string" + }, + "type": "array" + }, + "forward_enabled": { + "description": "Enable auto forwarding", + "type": "boolean" + }, + "forward_keep": { + "description": "Keep a copy of the forwarded email in the inbox", + "type": "boolean" + }, + "global_admin": { + "description": "Make the user a global administrator", + "type": "boolean" + }, + "quota_bytes": { + "description": "The maximum quota for the user\u2019s email box in bytes", + "example": "1000000000", + "type": "integer" + }, + "raw_password": { + "description": "The raw (plain text) password of the user. Mailu will hash the password using BCRYPT-SHA256", + "example": "secret", + "type": "string" + }, + "reply_body": { + "description": "The body of the automatic reply email", + "example": "Hello, I am out of office. I will respond when I am back.", + "type": "string" + }, + "reply_enabled": { + "description": "Enable automatic replies. This is also known as out of office (ooo) or out of facility (oof) replies", + "type": "boolean" + }, + "reply_enddate": { + "description": "End date for automatic replies in YYYY-MM-DD format.", + "example": "2022-02-22", + "format": "date", + "type": "string" + }, + "reply_startdate": { + "description": "Start date for automatic replies in YYYY-MM-DD format.", + "example": "2022-02-10", + "format": "date", + "type": "string" + }, + "reply_subject": { + "description": "Optional subject for the automatic reply", + "example": "Out of office", + "type": "string" + }, + "spam_enabled": { + "description": "Enable the spam filter", + "type": "boolean" + }, + "spam_mark_as_read": { + "description": "Enable marking spam mails as read", + "type": "boolean" + }, + "spam_threshold": { + "description": "The user defined spam filter tolerance", + "example": "80", + "type": "integer" + } + }, + "type": "object" + } + }, + "info": { + "title": "Mailu API", + "version": "1.0" + }, + "paths": { + "/alias": { + "get": { + "operationId": "list_alias", + "responses": { + "200": { + "description": "Success", + "schema": { + "items": { + "$ref": "#/definitions/Alias" + }, + "type": "array" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "List all aliases", + "tags": [ + "alias" + ] + }, + "post": { + "operationId": "create_alias", + "parameters": [ + { + "in": "body", + "name": "payload", + "required": true, + "schema": { + "$ref": "#/definitions/Alias" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Not found", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "409": { + "description": "Duplicate alias", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Create a new alias", + "tags": [ + "alias" + ] + } + }, + "/alias/destination/{domain}": { + "get": { + "operationId": "find_alias_filter_domain", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Alias" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Alias or domain not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Look up the aliases of the specified domain", + "tags": [ + "alias" + ] + }, + "parameters": [ + { + "in": "path", + "name": "domain", + "required": true, + "type": "string" + } + ] + }, + "/alias/{alias}": { + "delete": { + "operationId": "delete_alias", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Alias not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Delete the specified alias", + "tags": [ + "alias" + ] + }, + "get": { + "operationId": "find_alias", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Alias" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Alias not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Look up the specified alias", + "tags": [ + "alias" + ] + }, + "parameters": [ + { + "in": "path", + "name": "alias", + "required": true, + "type": "string" + } + ], + "patch": { + "operationId": "update_alias", + "parameters": [ + { + "in": "body", + "name": "payload", + "required": true, + "schema": { + "$ref": "#/definitions/AliasUpdate" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Alias not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Update the specfied alias", + "tags": [ + "alias" + ] + } + }, + "/alternative": { + "get": { + "operationId": "list_alternative", + "responses": { + "200": { + "description": "Success", + "schema": { + "items": { + "$ref": "#/definitions/AlternativeDomain" + }, + "type": "array" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "List all alternatives", + "tags": [ + "alternative" + ] + }, + "post": { + "operationId": "create_alternative", + "parameters": [ + { + "in": "body", + "name": "payload", + "required": true, + "schema": { + "$ref": "#/definitions/AlternativeDomain" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Domain not found or missing", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "409": { + "description": "Duplicate alternative domain name", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Create a new alternative (for domain)", + "tags": [ + "alternative" + ] + } + }, + "/alternative/{alt}": { + "delete": { + "operationId": "delete_alternative", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Alternative/Domain not found or missing", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "409": { + "description": "Duplicate domain name", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Delete the specified alternative (for domain)", + "tags": [ + "alternative" + ] + }, + "get": { + "operationId": "find_alternative", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/AlternativeDomain" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Alternative not found or missing", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Look up the specified alternative (of domain)", + "tags": [ + "alternative" + ] + }, + "parameters": [ + { + "in": "path", + "name": "alt", + "required": true, + "type": "string" + } + ] + }, + "/domain": { + "get": { + "operationId": "list_domain", + "responses": { + "200": { + "description": "Success", + "schema": { + "items": { + "$ref": "#/definitions/DomainGet" + }, + "type": "array" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "List all domains", + "tags": [ + "domain" + ] + }, + "post": { + "operationId": "create_domain", + "parameters": [ + { + "in": "body", + "name": "payload", + "required": true, + "schema": { + "$ref": "#/definitions/Domain" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "409": { + "description": "Duplicate domain/alternative name", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Create a new domain", + "tags": [ + "domain" + ] + } + }, + "/domain/{domain}": { + "delete": { + "operationId": "delete_domain", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Domain not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Delete the specified domain", + "tags": [ + "domain" + ] + }, + "get": { + "operationId": "find_domain", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/DomainGet" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Domain not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Look up the specified domain", + "tags": [ + "domain" + ] + }, + "parameters": [ + { + "in": "path", + "name": "domain", + "required": true, + "type": "string" + } + ], + "patch": { + "operationId": "update_domain", + "parameters": [ + { + "in": "body", + "name": "payload", + "required": true, + "schema": { + "$ref": "#/definitions/DomainUpdate" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Domain not found", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "409": { + "description": "Duplicate domain/alternative name", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Update the specified domain", + "tags": [ + "domain" + ] + } + }, + "/domain/{domain}/dkim": { + "parameters": [ + { + "in": "path", + "name": "domain", + "required": true, + "type": "string" + } + ], + "post": { + "operationId": "generate_dkim", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Domain not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Generate new DKIM/DMARC keys for the specified domain", + "tags": [ + "domain" + ] + } + }, + "/domain/{domain}/manager": { + "get": { + "operationId": "list_managers", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Manager" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "domain not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "List all managers of the specified domain", + "tags": [ + "domain" + ] + }, + "parameters": [ + { + "in": "path", + "name": "domain", + "required": true, + "type": "string" + } + ], + "post": { + "operationId": "create_manager", + "parameters": [ + { + "in": "body", + "name": "payload", + "required": true, + "schema": { + "$ref": "#/definitions/ManagerCreate" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "User or domain not found", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "409": { + "description": "Duplicate domain manager", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Create a new domain manager for the specified domain", + "tags": [ + "domain" + ] + } + }, + "/domain/{domain}/manager/{email}": { + "delete": { + "operationId": "delete_manager", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Manager not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Delete the specified manager of the specified domain", + "tags": [ + "domain" + ] + }, + "get": { + "operationId": "find_manager", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Manager not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Check if the specified user is a manager of the specified domain", + "tags": [ + "domain" + ] + }, + "parameters": [ + { + "in": "path", + "name": "domain", + "required": true, + "type": "string" + }, + { + "in": "path", + "name": "email", + "required": true, + "type": "string" + } + ] + }, + "/domain/{domain}/users": { + "get": { + "operationId": "list_user_domain", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/UserGet" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Domain not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "List all the users from the specified domain", + "tags": [ + "domain" + ] + }, + "parameters": [ + { + "in": "path", + "name": "domain", + "required": true, + "type": "string" + } + ] + }, + "/relay": { + "get": { + "operationId": "list_relays", + "responses": { + "200": { + "description": "Success", + "schema": { + "items": { + "$ref": "#/definitions/Relay" + }, + "type": "array" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "List all relays", + "tags": [ + "relay" + ] + }, + "post": { + "operationId": "create_relay", + "parameters": [ + { + "in": "body", + "name": "payload", + "required": true, + "schema": { + "$ref": "#/definitions/Relay" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "409": { + "description": "Duplicate relay", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Create a new relay", + "tags": [ + "relay" + ] + } + }, + "/relay/{name}": { + "delete": { + "operationId": "delete_relay", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Relay not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Delete the specified relay", + "tags": [ + "relay" + ] + }, + "get": { + "operationId": "find_relay", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Relay" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Relay not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Look up the specified relay", + "tags": [ + "relay" + ] + }, + "parameters": [ + { + "in": "path", + "name": "name", + "required": true, + "type": "string" + } + ], + "patch": { + "operationId": "update_relay", + "parameters": [ + { + "in": "body", + "name": "payload", + "required": true, + "schema": { + "$ref": "#/definitions/RelayUpdate" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Relay not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Update the specified relay", + "tags": [ + "relay" + ] + } + }, + "/token": { + "get": { + "operationId": "list_tokens", + "responses": { + "200": { + "description": "Success", + "schema": { + "items": { + "$ref": "#/definitions/TokenGetResponse" + }, + "type": "array" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "List all tokens", + "tags": [ + "token" + ] + }, + "post": { + "operationId": "create_token", + "parameters": [ + { + "in": "body", + "name": "payload", + "required": true, + "schema": { + "$ref": "#/definitions/TokenPost" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/TokenPostResponse" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "User not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Create a new token", + "tags": [ + "token" + ] + } + }, + "/token/{token_id}": { + "delete": { + "operationId": "delete_token", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Token not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Delete the specified token", + "tags": [ + "token" + ] + }, + "get": { + "operationId": "find_token", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/TokenGetResponse" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Token not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Find the specified token", + "tags": [ + "token" + ] + }, + "parameters": [ + { + "in": "path", + "name": "token_id", + "required": true, + "type": "string" + } + ], + "patch": { + "operationId": "update_token", + "parameters": [ + { + "in": "body", + "name": "payload", + "required": true, + "schema": { + "$ref": "#/definitions/TokenPost2" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "User not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Update the specified token", + "tags": [ + "token" + ] + } + }, + "/tokenuser/{email}": { + "get": { + "operationId": "find_tokens_of_user", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/TokenGetResponse" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "Token not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Look up all the tokens of the specified user", + "tags": [ + "token" + ] + }, + "parameters": [ + { + "in": "path", + "name": "email", + "required": true, + "type": "string" + } + ], + "post": { + "operationId": "create_token", + "parameters": [ + { + "in": "body", + "name": "payload", + "required": true, + "schema": { + "$ref": "#/definitions/TokenPost2" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/TokenPostResponse" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "User not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Create a new token for the specified user", + "tags": [ + "token" + ] + } + }, + "/user": { + "get": { + "operationId": "list_user", + "responses": { + "200": { + "description": "Success", + "schema": { + "items": { + "$ref": "#/definitions/UserGet" + }, + "type": "array" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "List all users", + "tags": [ + "user" + ] + }, + "post": { + "operationId": "create_user", + "parameters": [ + { + "in": "body", + "name": "payload", + "required": true, + "schema": { + "$ref": "#/definitions/UserCreate" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "409": { + "description": "Duplicate user", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Create a new user", + "tags": [ + "user" + ] + } + }, + "/user/{email}": { + "delete": { + "operationId": "delete_user", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "User not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Delete the specified user", + "tags": [ + "user" + ] + }, + "get": { + "operationId": "find_user", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/UserGet" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "User not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Look up the specified user", + "tags": [ + "user" + ] + }, + "parameters": [ + { + "in": "path", + "name": "email", + "required": true, + "type": "string" + } + ], + "patch": { + "operationId": "update_user", + "parameters": [ + { + "in": "body", + "name": "payload", + "required": true, + "schema": { + "$ref": "#/definitions/UserUpdate" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "400": { + "description": "Input validation exception", + "schema": { + "$ref": "#/definitions/Response" + } + }, + "401": { + "description": "Authorization header missing" + }, + "403": { + "description": "Invalid authorization header" + }, + "404": { + "description": "User not found", + "schema": { + "$ref": "#/definitions/Response" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Update the specified user", + "tags": [ + "user" + ] + } + } + }, + "produces": [ + "application/json" + ], + "responses": { + "MaskError": { + "description": "When any error occurs on mask" + }, + "ParseError": { + "description": "When a mask can't be parsed" + } + }, + "security": [ + { + "Bearer": [] + } + ], + "securityDefinitions": { + "Bearer": { + "in": "header", + "name": "Authorization", + "type": "apiKey" + } + }, + "swagger": "2.0", + "tags": [ + { + "description": "User operations", + "name": "user" + }, + { + "description": "Domain operations", + "name": "domain" + }, + { + "description": "Alternative operations", + "name": "alternative" + }, + { + "description": "Alias operations", + "name": "alias" + }, + { + "description": "Relay operations", + "name": "relay" + }, + { + "description": "Token operations", + "name": "token" + } + ] +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..086fbda --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +# a2a-pack is installed by the platform base image. +deepagents>=0.5.0 +langchain>=0.3 +langchain-openai>=0.2 +langchain-core>=0.3 +langgraph>=0.6 +httpx>=0.27