dominusnode-dify 1.1.0__tar.gz → 1.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dominusnode-dify might be problematic. Click here for more details.
- {dominusnode_dify-1.1.0 → dominusnode_dify-1.2.0}/PKG-INFO +1 -1
- {dominusnode_dify-1.1.0 → dominusnode_dify-1.2.0}/dominusnode_dify/tools.py +944 -3
- {dominusnode_dify-1.1.0 → dominusnode_dify-1.2.0}/dominusnode_dify.egg-info/PKG-INFO +1 -1
- {dominusnode_dify-1.1.0 → dominusnode_dify-1.2.0}/pyproject.toml +1 -1
- {dominusnode_dify-1.1.0 → dominusnode_dify-1.2.0}/LICENSE +0 -0
- {dominusnode_dify-1.1.0 → dominusnode_dify-1.2.0}/README.md +0 -0
- {dominusnode_dify-1.1.0 → dominusnode_dify-1.2.0}/dominusnode_dify/__init__.py +0 -0
- {dominusnode_dify-1.1.0 → dominusnode_dify-1.2.0}/dominusnode_dify.egg-info/SOURCES.txt +0 -0
- {dominusnode_dify-1.1.0 → dominusnode_dify-1.2.0}/dominusnode_dify.egg-info/dependency_links.txt +0 -0
- {dominusnode_dify-1.1.0 → dominusnode_dify-1.2.0}/dominusnode_dify.egg-info/requires.txt +0 -0
- {dominusnode_dify-1.1.0 → dominusnode_dify-1.2.0}/dominusnode_dify.egg-info/top_level.txt +0 -0
- {dominusnode_dify-1.1.0 → dominusnode_dify-1.2.0}/setup.cfg +0 -0
- {dominusnode_dify-1.1.0 → dominusnode_dify-1.2.0}/tests/test_tools.py +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Dominus Node Dify tool provider --
|
|
1
|
+
"""Dominus Node Dify tool provider -- 53 tools with full security hardening.
|
|
2
2
|
|
|
3
3
|
This module implements the DominusNodeProvider class that Dify invokes.
|
|
4
4
|
Each tool is a method that receives credentials (from the provider YAML)
|
|
@@ -393,6 +393,8 @@ _DOMAIN_RE = re.compile(
|
|
|
393
393
|
r"^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$"
|
|
394
394
|
)
|
|
395
395
|
|
|
396
|
+
_BASIC_EMAIL_RE = re.compile(r"^[^\s@]+@[^\s@]+\.[^\s@]+$")
|
|
397
|
+
|
|
396
398
|
|
|
397
399
|
# ---------------------------------------------------------------------------
|
|
398
400
|
# Period to date range helper
|
|
@@ -477,7 +479,7 @@ def _validate_allowed_domains(domains: Any) -> Optional[str]:
|
|
|
477
479
|
class DominusNodeProvider:
|
|
478
480
|
"""Dify tool provider for Dominus Node rotating proxy service.
|
|
479
481
|
|
|
480
|
-
Authenticates using a Dominus Node API key and exposes
|
|
482
|
+
Authenticates using a Dominus Node API key and exposes 53 tool methods that
|
|
481
483
|
Dify can invoke. Each tool method receives ``credentials`` and ``params``
|
|
482
484
|
dicts from the Dify runtime and returns a dict result.
|
|
483
485
|
|
|
@@ -646,6 +648,69 @@ class DominusNodeProvider:
|
|
|
646
648
|
return self._api_request(method, path, body)
|
|
647
649
|
raise
|
|
648
650
|
|
|
651
|
+
# ------------------------------------------------------------------
|
|
652
|
+
# Unauthenticated request helper (register, login, verify_email)
|
|
653
|
+
# ------------------------------------------------------------------
|
|
654
|
+
|
|
655
|
+
def _unauthenticated_request(
|
|
656
|
+
self,
|
|
657
|
+
method: str,
|
|
658
|
+
path: str,
|
|
659
|
+
body: Optional[Dict[str, Any]] = None,
|
|
660
|
+
) -> Any:
|
|
661
|
+
"""Make an unauthenticated API request (no Bearer token).
|
|
662
|
+
|
|
663
|
+
Used for register, login, and verify_email endpoints.
|
|
664
|
+
|
|
665
|
+
Args:
|
|
666
|
+
method: HTTP method (POST).
|
|
667
|
+
path: API path (e.g., ``/api/auth/register``).
|
|
668
|
+
body: Optional JSON body.
|
|
669
|
+
|
|
670
|
+
Returns:
|
|
671
|
+
Parsed JSON response (with dangerous keys stripped).
|
|
672
|
+
|
|
673
|
+
Raises:
|
|
674
|
+
RuntimeError: On API errors or oversized responses.
|
|
675
|
+
"""
|
|
676
|
+
headers: Dict[str, str] = {
|
|
677
|
+
"User-Agent": "dominusnode-dify/1.0.0",
|
|
678
|
+
"Content-Type": "application/json",
|
|
679
|
+
}
|
|
680
|
+
if self.agent_secret:
|
|
681
|
+
headers["X-DominusNode-Agent"] = "mcp"
|
|
682
|
+
headers["X-DominusNode-Agent-Secret"] = self.agent_secret
|
|
683
|
+
|
|
684
|
+
with httpx.Client(
|
|
685
|
+
timeout=self.timeout,
|
|
686
|
+
follow_redirects=False,
|
|
687
|
+
) as client:
|
|
688
|
+
kwargs: Dict[str, Any] = {"headers": headers}
|
|
689
|
+
if body is not None:
|
|
690
|
+
kwargs["json"] = body
|
|
691
|
+
|
|
692
|
+
resp = client.request(method, f"{self.base_url}{path}", **kwargs)
|
|
693
|
+
|
|
694
|
+
if len(resp.content) > MAX_RESPONSE_BYTES:
|
|
695
|
+
raise RuntimeError("Response body too large")
|
|
696
|
+
|
|
697
|
+
if resp.status_code >= 400:
|
|
698
|
+
try:
|
|
699
|
+
err_data = resp.json()
|
|
700
|
+
msg = err_data.get("error", resp.text)
|
|
701
|
+
except Exception:
|
|
702
|
+
msg = resp.text
|
|
703
|
+
msg = str(msg)[:500]
|
|
704
|
+
raise RuntimeError(
|
|
705
|
+
f"API error {resp.status_code}: {_sanitize_error(msg)}"
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
if resp.text:
|
|
709
|
+
data = resp.json()
|
|
710
|
+
_strip_dangerous_keys(data)
|
|
711
|
+
return data
|
|
712
|
+
return {}
|
|
713
|
+
|
|
649
714
|
# ==================================================================
|
|
650
715
|
# Tool 1: proxied_fetch
|
|
651
716
|
# ==================================================================
|
|
@@ -1585,6 +1650,827 @@ class DominusNodeProvider:
|
|
|
1585
1650
|
except Exception as e:
|
|
1586
1651
|
return {"error": _sanitize_error(str(e))}
|
|
1587
1652
|
|
|
1653
|
+
# ==================================================================
|
|
1654
|
+
# Tool 26: get_proxy_status
|
|
1655
|
+
# ==================================================================
|
|
1656
|
+
|
|
1657
|
+
def get_proxy_status(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
1658
|
+
"""Get the current status of the proxy gateway.
|
|
1659
|
+
|
|
1660
|
+
Args:
|
|
1661
|
+
params: Empty dict (no parameters needed).
|
|
1662
|
+
|
|
1663
|
+
Returns:
|
|
1664
|
+
Dict with proxy status information.
|
|
1665
|
+
"""
|
|
1666
|
+
try:
|
|
1667
|
+
result = self._request_with_retry("GET", "/api/proxy/status")
|
|
1668
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
1669
|
+
except Exception as e:
|
|
1670
|
+
return {"error": _sanitize_error(str(e))}
|
|
1671
|
+
|
|
1672
|
+
# ==================================================================
|
|
1673
|
+
# Tool 27: get_transactions
|
|
1674
|
+
# ==================================================================
|
|
1675
|
+
|
|
1676
|
+
def get_transactions(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
1677
|
+
"""Get wallet transaction history.
|
|
1678
|
+
|
|
1679
|
+
Args:
|
|
1680
|
+
params: Dict with optional key: limit (1-100, default 20).
|
|
1681
|
+
|
|
1682
|
+
Returns:
|
|
1683
|
+
Dict with wallet transactions.
|
|
1684
|
+
"""
|
|
1685
|
+
limit = params.get("limit", 20)
|
|
1686
|
+
# Dify sends numbers as float; coerce to int
|
|
1687
|
+
if isinstance(limit, float) and limit == int(limit):
|
|
1688
|
+
limit = int(limit)
|
|
1689
|
+
|
|
1690
|
+
if not isinstance(limit, int) or isinstance(limit, bool) or limit < 1 or limit > 100:
|
|
1691
|
+
return {"error": "limit must be an integer between 1 and 100"}
|
|
1692
|
+
|
|
1693
|
+
qs = f"?limit={limit}"
|
|
1694
|
+
|
|
1695
|
+
try:
|
|
1696
|
+
result = self._request_with_retry("GET", f"/api/wallet/transactions{qs}")
|
|
1697
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
1698
|
+
except Exception as e:
|
|
1699
|
+
return {"error": _sanitize_error(str(e))}
|
|
1700
|
+
|
|
1701
|
+
# ==================================================================
|
|
1702
|
+
# Tool 28: get_forecast
|
|
1703
|
+
# ==================================================================
|
|
1704
|
+
|
|
1705
|
+
def get_forecast(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
1706
|
+
"""Get wallet balance forecast (estimated days remaining, depletion date).
|
|
1707
|
+
|
|
1708
|
+
Args:
|
|
1709
|
+
params: Empty dict (no parameters needed).
|
|
1710
|
+
|
|
1711
|
+
Returns:
|
|
1712
|
+
Dict with forecast information.
|
|
1713
|
+
"""
|
|
1714
|
+
try:
|
|
1715
|
+
result = self._request_with_retry("GET", "/api/wallet/forecast")
|
|
1716
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
1717
|
+
except Exception as e:
|
|
1718
|
+
return {"error": _sanitize_error(str(e))}
|
|
1719
|
+
|
|
1720
|
+
# ==================================================================
|
|
1721
|
+
# Tool 29: check_payment
|
|
1722
|
+
# ==================================================================
|
|
1723
|
+
|
|
1724
|
+
def check_payment(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
1725
|
+
"""Check the status of a crypto payment invoice.
|
|
1726
|
+
|
|
1727
|
+
Args:
|
|
1728
|
+
params: Dict with key: invoice_id (required).
|
|
1729
|
+
|
|
1730
|
+
Returns:
|
|
1731
|
+
Dict with payment status information.
|
|
1732
|
+
"""
|
|
1733
|
+
invoice_id = params.get("invoice_id")
|
|
1734
|
+
if not invoice_id or not isinstance(invoice_id, str):
|
|
1735
|
+
return {"error": "invoice_id is required and must be a string"}
|
|
1736
|
+
|
|
1737
|
+
invoice_id = invoice_id.strip()
|
|
1738
|
+
if not invoice_id:
|
|
1739
|
+
return {"error": "invoice_id is required and must be a string"}
|
|
1740
|
+
|
|
1741
|
+
if any(0 <= ord(c) <= 0x1F or ord(c) == 0x7F for c in invoice_id):
|
|
1742
|
+
return {"error": "invoice_id contains invalid control characters"}
|
|
1743
|
+
|
|
1744
|
+
try:
|
|
1745
|
+
result = self._request_with_retry(
|
|
1746
|
+
"GET",
|
|
1747
|
+
f"/api/wallet/topup/crypto/{quote(invoice_id, safe='')}/status",
|
|
1748
|
+
)
|
|
1749
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
1750
|
+
except Exception as e:
|
|
1751
|
+
return {"error": _sanitize_error(str(e))}
|
|
1752
|
+
|
|
1753
|
+
# ==================================================================
|
|
1754
|
+
# Tool 30: get_daily_usage
|
|
1755
|
+
# ==================================================================
|
|
1756
|
+
|
|
1757
|
+
def get_daily_usage(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
1758
|
+
"""Get daily bandwidth usage statistics.
|
|
1759
|
+
|
|
1760
|
+
Args:
|
|
1761
|
+
params: Dict with optional key: days (1-365, default 7).
|
|
1762
|
+
|
|
1763
|
+
Returns:
|
|
1764
|
+
Dict with daily usage breakdown.
|
|
1765
|
+
"""
|
|
1766
|
+
days = params.get("days", 7)
|
|
1767
|
+
# Dify sends numbers as float; coerce to int
|
|
1768
|
+
if isinstance(days, float) and days == int(days):
|
|
1769
|
+
days = int(days)
|
|
1770
|
+
|
|
1771
|
+
if not isinstance(days, int) or isinstance(days, bool) or days < 1 or days > 365:
|
|
1772
|
+
return {"error": "days must be an integer between 1 and 365"}
|
|
1773
|
+
|
|
1774
|
+
try:
|
|
1775
|
+
result = self._request_with_retry("GET", f"/api/usage/daily?days={days}")
|
|
1776
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
1777
|
+
except Exception as e:
|
|
1778
|
+
return {"error": _sanitize_error(str(e))}
|
|
1779
|
+
|
|
1780
|
+
# ==================================================================
|
|
1781
|
+
# Tool 31: get_top_hosts
|
|
1782
|
+
# ==================================================================
|
|
1783
|
+
|
|
1784
|
+
def get_top_hosts(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
1785
|
+
"""Get top hosts by bandwidth usage.
|
|
1786
|
+
|
|
1787
|
+
Args:
|
|
1788
|
+
params: Dict with optional key: limit (1-100, default 10).
|
|
1789
|
+
|
|
1790
|
+
Returns:
|
|
1791
|
+
Dict with top hosts ranked by bandwidth.
|
|
1792
|
+
"""
|
|
1793
|
+
limit = params.get("limit", 10)
|
|
1794
|
+
# Dify sends numbers as float; coerce to int
|
|
1795
|
+
if isinstance(limit, float) and limit == int(limit):
|
|
1796
|
+
limit = int(limit)
|
|
1797
|
+
|
|
1798
|
+
if not isinstance(limit, int) or isinstance(limit, bool) or limit < 1 or limit > 100:
|
|
1799
|
+
return {"error": "limit must be an integer between 1 and 100"}
|
|
1800
|
+
|
|
1801
|
+
try:
|
|
1802
|
+
result = self._request_with_retry("GET", f"/api/usage/top-hosts?limit={limit}")
|
|
1803
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
1804
|
+
except Exception as e:
|
|
1805
|
+
return {"error": _sanitize_error(str(e))}
|
|
1806
|
+
|
|
1807
|
+
# ==================================================================
|
|
1808
|
+
# Tool 32: register (unauthenticated)
|
|
1809
|
+
# ==================================================================
|
|
1810
|
+
|
|
1811
|
+
def register(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
1812
|
+
"""Register a new Dominus Node account.
|
|
1813
|
+
|
|
1814
|
+
This is an unauthenticated endpoint -- no API key or token needed.
|
|
1815
|
+
|
|
1816
|
+
Args:
|
|
1817
|
+
params: Dict with keys: email (required), password (required, 8-128 chars).
|
|
1818
|
+
|
|
1819
|
+
Returns:
|
|
1820
|
+
Dict with registration result.
|
|
1821
|
+
"""
|
|
1822
|
+
email = params.get("email")
|
|
1823
|
+
password = params.get("password")
|
|
1824
|
+
|
|
1825
|
+
if not email or not isinstance(email, str):
|
|
1826
|
+
return {"error": "email is required and must be a string"}
|
|
1827
|
+
if not _BASIC_EMAIL_RE.match(email):
|
|
1828
|
+
return {"error": "email must be a valid email address"}
|
|
1829
|
+
if len(email) > 254:
|
|
1830
|
+
return {"error": "email must be 254 characters or fewer"}
|
|
1831
|
+
|
|
1832
|
+
if not password or not isinstance(password, str):
|
|
1833
|
+
return {"error": "password is required and must be a string"}
|
|
1834
|
+
if len(password) < 8:
|
|
1835
|
+
return {"error": "password must be at least 8 characters"}
|
|
1836
|
+
if len(password) > 128:
|
|
1837
|
+
return {"error": "password must be 128 characters or fewer"}
|
|
1838
|
+
|
|
1839
|
+
try:
|
|
1840
|
+
result = self._unauthenticated_request(
|
|
1841
|
+
"POST",
|
|
1842
|
+
"/api/auth/register",
|
|
1843
|
+
{"email": email, "password": password},
|
|
1844
|
+
)
|
|
1845
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
1846
|
+
except Exception as e:
|
|
1847
|
+
return {"error": _sanitize_error(str(e))}
|
|
1848
|
+
|
|
1849
|
+
# ==================================================================
|
|
1850
|
+
# Tool 33: login (unauthenticated)
|
|
1851
|
+
# ==================================================================
|
|
1852
|
+
|
|
1853
|
+
def login(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
1854
|
+
"""Log in to a Dominus Node account.
|
|
1855
|
+
|
|
1856
|
+
This is an unauthenticated endpoint -- no API key or token needed.
|
|
1857
|
+
Detects MFA challenges and surfaces them to the caller.
|
|
1858
|
+
|
|
1859
|
+
Args:
|
|
1860
|
+
params: Dict with keys: email (required), password (required).
|
|
1861
|
+
|
|
1862
|
+
Returns:
|
|
1863
|
+
Dict with login result (tokens or MFA challenge).
|
|
1864
|
+
"""
|
|
1865
|
+
email = params.get("email")
|
|
1866
|
+
password = params.get("password")
|
|
1867
|
+
|
|
1868
|
+
if not email or not isinstance(email, str):
|
|
1869
|
+
return {"error": "email is required and must be a string"}
|
|
1870
|
+
if not _BASIC_EMAIL_RE.match(email):
|
|
1871
|
+
return {"error": "email must be a valid email address"}
|
|
1872
|
+
if len(email) > 254:
|
|
1873
|
+
return {"error": "email must be 254 characters or fewer"}
|
|
1874
|
+
|
|
1875
|
+
if not password or not isinstance(password, str):
|
|
1876
|
+
return {"error": "password is required and must be a string"}
|
|
1877
|
+
if len(password) < 1:
|
|
1878
|
+
return {"error": "password is required"}
|
|
1879
|
+
if len(password) > 128:
|
|
1880
|
+
return {"error": "password must be 128 characters or fewer"}
|
|
1881
|
+
|
|
1882
|
+
try:
|
|
1883
|
+
result = self._unauthenticated_request(
|
|
1884
|
+
"POST",
|
|
1885
|
+
"/api/auth/login",
|
|
1886
|
+
{"email": email, "password": password},
|
|
1887
|
+
)
|
|
1888
|
+
if isinstance(result, dict) and result.get("mfaRequired"):
|
|
1889
|
+
return {
|
|
1890
|
+
"mfaRequired": True,
|
|
1891
|
+
"challengeToken": result.get("challengeToken"),
|
|
1892
|
+
"message": "Multi-factor authentication is required. Complete the MFA challenge to proceed.",
|
|
1893
|
+
}
|
|
1894
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
1895
|
+
except Exception as e:
|
|
1896
|
+
return {"error": _sanitize_error(str(e))}
|
|
1897
|
+
|
|
1898
|
+
# ==================================================================
|
|
1899
|
+
# Tool 34: get_account_info
|
|
1900
|
+
# ==================================================================
|
|
1901
|
+
|
|
1902
|
+
def get_account_info(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
1903
|
+
"""Get current account information.
|
|
1904
|
+
|
|
1905
|
+
Args:
|
|
1906
|
+
params: Empty dict (no parameters needed).
|
|
1907
|
+
|
|
1908
|
+
Returns:
|
|
1909
|
+
Dict with account details (id, email, plan, status, etc.).
|
|
1910
|
+
"""
|
|
1911
|
+
try:
|
|
1912
|
+
result = self._request_with_retry("GET", "/api/auth/me")
|
|
1913
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
1914
|
+
except Exception as e:
|
|
1915
|
+
return {"error": _sanitize_error(str(e))}
|
|
1916
|
+
|
|
1917
|
+
# ==================================================================
|
|
1918
|
+
# Tool 35: verify_email (unauthenticated)
|
|
1919
|
+
# ==================================================================
|
|
1920
|
+
|
|
1921
|
+
def verify_email(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
1922
|
+
"""Verify an email address using a verification token.
|
|
1923
|
+
|
|
1924
|
+
This is an unauthenticated endpoint.
|
|
1925
|
+
|
|
1926
|
+
Args:
|
|
1927
|
+
params: Dict with key: token (required).
|
|
1928
|
+
|
|
1929
|
+
Returns:
|
|
1930
|
+
Dict confirming email verification.
|
|
1931
|
+
"""
|
|
1932
|
+
token = params.get("token")
|
|
1933
|
+
if not token or not isinstance(token, str):
|
|
1934
|
+
return {"error": "token is required and must be a string"}
|
|
1935
|
+
|
|
1936
|
+
token = token.strip()
|
|
1937
|
+
if not token:
|
|
1938
|
+
return {"error": "token is required and must be a string"}
|
|
1939
|
+
|
|
1940
|
+
if any(0 <= ord(c) <= 0x1F or ord(c) == 0x7F for c in token):
|
|
1941
|
+
return {"error": "token contains invalid control characters"}
|
|
1942
|
+
|
|
1943
|
+
try:
|
|
1944
|
+
result = self._unauthenticated_request(
|
|
1945
|
+
"POST",
|
|
1946
|
+
"/api/auth/verify-email",
|
|
1947
|
+
{"token": token},
|
|
1948
|
+
)
|
|
1949
|
+
return result if isinstance(result, dict) else {"message": "Email verified successfully"}
|
|
1950
|
+
except Exception as e:
|
|
1951
|
+
return {"error": _sanitize_error(str(e))}
|
|
1952
|
+
|
|
1953
|
+
# ==================================================================
|
|
1954
|
+
# Tool 36: resend_verification
|
|
1955
|
+
# ==================================================================
|
|
1956
|
+
|
|
1957
|
+
def resend_verification(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
1958
|
+
"""Resend the email verification message.
|
|
1959
|
+
|
|
1960
|
+
Args:
|
|
1961
|
+
params: Empty dict (no parameters needed).
|
|
1962
|
+
|
|
1963
|
+
Returns:
|
|
1964
|
+
Dict confirming the verification email was sent.
|
|
1965
|
+
"""
|
|
1966
|
+
try:
|
|
1967
|
+
result = self._request_with_retry("POST", "/api/auth/resend-verification")
|
|
1968
|
+
return result if isinstance(result, dict) else {"message": "Verification email sent"}
|
|
1969
|
+
except Exception as e:
|
|
1970
|
+
return {"error": _sanitize_error(str(e))}
|
|
1971
|
+
|
|
1972
|
+
# ==================================================================
|
|
1973
|
+
# Tool 37: update_password
|
|
1974
|
+
# ==================================================================
|
|
1975
|
+
|
|
1976
|
+
def update_password(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
1977
|
+
"""Update the current account password.
|
|
1978
|
+
|
|
1979
|
+
Args:
|
|
1980
|
+
params: Dict with keys: current_password (required),
|
|
1981
|
+
new_password (required, 8-128 chars).
|
|
1982
|
+
|
|
1983
|
+
Returns:
|
|
1984
|
+
Dict confirming the password was updated.
|
|
1985
|
+
"""
|
|
1986
|
+
current_password = params.get("current_password")
|
|
1987
|
+
new_password = params.get("new_password")
|
|
1988
|
+
|
|
1989
|
+
if not current_password or not isinstance(current_password, str):
|
|
1990
|
+
return {"error": "current_password is required and must be a string"}
|
|
1991
|
+
if len(current_password) < 1:
|
|
1992
|
+
return {"error": "current_password is required"}
|
|
1993
|
+
|
|
1994
|
+
if not new_password or not isinstance(new_password, str):
|
|
1995
|
+
return {"error": "new_password is required and must be a string"}
|
|
1996
|
+
if len(new_password) < 8:
|
|
1997
|
+
return {"error": "new_password must be at least 8 characters"}
|
|
1998
|
+
if len(new_password) > 128:
|
|
1999
|
+
return {"error": "new_password must be 128 characters or fewer"}
|
|
2000
|
+
|
|
2001
|
+
try:
|
|
2002
|
+
result = self._request_with_retry(
|
|
2003
|
+
"POST",
|
|
2004
|
+
"/api/auth/change-password",
|
|
2005
|
+
{"currentPassword": current_password, "newPassword": new_password},
|
|
2006
|
+
)
|
|
2007
|
+
return result if isinstance(result, dict) else {"message": "Password updated successfully"}
|
|
2008
|
+
except Exception as e:
|
|
2009
|
+
return {"error": _sanitize_error(str(e))}
|
|
2010
|
+
|
|
2011
|
+
# ==================================================================
|
|
2012
|
+
# Tool 38: list_keys
|
|
2013
|
+
# ==================================================================
|
|
2014
|
+
|
|
2015
|
+
def list_keys(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2016
|
+
"""List all API keys for the current account.
|
|
2017
|
+
|
|
2018
|
+
Args:
|
|
2019
|
+
params: Empty dict (no parameters needed).
|
|
2020
|
+
|
|
2021
|
+
Returns:
|
|
2022
|
+
Dict with list of API keys.
|
|
2023
|
+
"""
|
|
2024
|
+
try:
|
|
2025
|
+
result = self._request_with_retry("GET", "/api/keys")
|
|
2026
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
2027
|
+
except Exception as e:
|
|
2028
|
+
return {"error": _sanitize_error(str(e))}
|
|
2029
|
+
|
|
2030
|
+
# ==================================================================
|
|
2031
|
+
# Tool 39: create_key
|
|
2032
|
+
# ==================================================================
|
|
2033
|
+
|
|
2034
|
+
def create_key(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2035
|
+
"""Create a new API key for the current account.
|
|
2036
|
+
|
|
2037
|
+
Args:
|
|
2038
|
+
params: Dict with optional key: label (string, 1-100 chars).
|
|
2039
|
+
|
|
2040
|
+
Returns:
|
|
2041
|
+
Dict with the created key details (key shown once).
|
|
2042
|
+
"""
|
|
2043
|
+
label = params.get("label")
|
|
2044
|
+
|
|
2045
|
+
body: Dict[str, Any] = {}
|
|
2046
|
+
if label is not None:
|
|
2047
|
+
if not isinstance(label, str):
|
|
2048
|
+
return {"error": "label must be a string"}
|
|
2049
|
+
err = _validate_label(label, "label")
|
|
2050
|
+
if err:
|
|
2051
|
+
return {"error": err}
|
|
2052
|
+
body["label"] = label
|
|
2053
|
+
|
|
2054
|
+
try:
|
|
2055
|
+
result = self._request_with_retry("POST", "/api/keys", body)
|
|
2056
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
2057
|
+
except Exception as e:
|
|
2058
|
+
return {"error": _sanitize_error(str(e))}
|
|
2059
|
+
|
|
2060
|
+
# ==================================================================
|
|
2061
|
+
# Tool 40: revoke_key
|
|
2062
|
+
# ==================================================================
|
|
2063
|
+
|
|
2064
|
+
def revoke_key(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2065
|
+
"""Revoke (delete) an API key by its ID.
|
|
2066
|
+
|
|
2067
|
+
Args:
|
|
2068
|
+
params: Dict with key: key_id (required, UUID).
|
|
2069
|
+
|
|
2070
|
+
Returns:
|
|
2071
|
+
Dict confirming the key was revoked.
|
|
2072
|
+
"""
|
|
2073
|
+
key_id = params.get("key_id")
|
|
2074
|
+
err = _validate_uuid(key_id, "key_id")
|
|
2075
|
+
if err:
|
|
2076
|
+
return {"error": err}
|
|
2077
|
+
|
|
2078
|
+
try:
|
|
2079
|
+
result = self._request_with_retry(
|
|
2080
|
+
"DELETE",
|
|
2081
|
+
f"/api/keys/{quote(key_id, safe='')}",
|
|
2082
|
+
)
|
|
2083
|
+
return result if isinstance(result, dict) else {"message": "API key revoked"}
|
|
2084
|
+
except Exception as e:
|
|
2085
|
+
return {"error": _sanitize_error(str(e))}
|
|
2086
|
+
|
|
2087
|
+
# ==================================================================
|
|
2088
|
+
# Tool 41: get_plan
|
|
2089
|
+
# ==================================================================
|
|
2090
|
+
|
|
2091
|
+
def get_plan(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2092
|
+
"""Get the current user's plan details.
|
|
2093
|
+
|
|
2094
|
+
Args:
|
|
2095
|
+
params: Empty dict (no parameters needed).
|
|
2096
|
+
|
|
2097
|
+
Returns:
|
|
2098
|
+
Dict with the current plan information.
|
|
2099
|
+
"""
|
|
2100
|
+
try:
|
|
2101
|
+
result = self._request_with_retry("GET", "/api/plans/user/plan")
|
|
2102
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
2103
|
+
except Exception as e:
|
|
2104
|
+
return {"error": _sanitize_error(str(e))}
|
|
2105
|
+
|
|
2106
|
+
# ==================================================================
|
|
2107
|
+
# Tool 42: list_plans
|
|
2108
|
+
# ==================================================================
|
|
2109
|
+
|
|
2110
|
+
def list_plans(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2111
|
+
"""List all available plans.
|
|
2112
|
+
|
|
2113
|
+
Args:
|
|
2114
|
+
params: Empty dict (no parameters needed).
|
|
2115
|
+
|
|
2116
|
+
Returns:
|
|
2117
|
+
Dict with list of available plans.
|
|
2118
|
+
"""
|
|
2119
|
+
try:
|
|
2120
|
+
result = self._request_with_retry("GET", "/api/plans")
|
|
2121
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
2122
|
+
except Exception as e:
|
|
2123
|
+
return {"error": _sanitize_error(str(e))}
|
|
2124
|
+
|
|
2125
|
+
# ==================================================================
|
|
2126
|
+
# Tool 43: change_plan
|
|
2127
|
+
# ==================================================================
|
|
2128
|
+
|
|
2129
|
+
def change_plan(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2130
|
+
"""Change the current user's plan.
|
|
2131
|
+
|
|
2132
|
+
Args:
|
|
2133
|
+
params: Dict with key: plan_id (required).
|
|
2134
|
+
|
|
2135
|
+
Returns:
|
|
2136
|
+
Dict confirming the plan change.
|
|
2137
|
+
"""
|
|
2138
|
+
plan_id = params.get("plan_id")
|
|
2139
|
+
if not plan_id or not isinstance(plan_id, str):
|
|
2140
|
+
return {"error": "plan_id is required and must be a string"}
|
|
2141
|
+
|
|
2142
|
+
plan_id = plan_id.strip()
|
|
2143
|
+
if not plan_id:
|
|
2144
|
+
return {"error": "plan_id is required and must be a string"}
|
|
2145
|
+
|
|
2146
|
+
if any(0 <= ord(c) <= 0x1F or ord(c) == 0x7F for c in plan_id):
|
|
2147
|
+
return {"error": "plan_id contains invalid control characters"}
|
|
2148
|
+
|
|
2149
|
+
try:
|
|
2150
|
+
result = self._request_with_retry(
|
|
2151
|
+
"PUT",
|
|
2152
|
+
"/api/plans/user/plan",
|
|
2153
|
+
{"planId": plan_id},
|
|
2154
|
+
)
|
|
2155
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
2156
|
+
except Exception as e:
|
|
2157
|
+
return {"error": _sanitize_error(str(e))}
|
|
2158
|
+
|
|
2159
|
+
# ==================================================================
|
|
2160
|
+
# Tool 44: team_delete
|
|
2161
|
+
# ==================================================================
|
|
2162
|
+
|
|
2163
|
+
def team_delete(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2164
|
+
"""Delete a team permanently.
|
|
2165
|
+
|
|
2166
|
+
Args:
|
|
2167
|
+
params: Dict with key: team_id (required, UUID).
|
|
2168
|
+
|
|
2169
|
+
Returns:
|
|
2170
|
+
Dict confirming the team was deleted.
|
|
2171
|
+
"""
|
|
2172
|
+
team_id = params.get("team_id")
|
|
2173
|
+
err = _validate_uuid(team_id, "team_id")
|
|
2174
|
+
if err:
|
|
2175
|
+
return {"error": err}
|
|
2176
|
+
|
|
2177
|
+
try:
|
|
2178
|
+
result = self._request_with_retry(
|
|
2179
|
+
"DELETE",
|
|
2180
|
+
f"/api/teams/{quote(team_id, safe='')}",
|
|
2181
|
+
)
|
|
2182
|
+
return result if isinstance(result, dict) else {"message": "Team deleted"}
|
|
2183
|
+
except Exception as e:
|
|
2184
|
+
return {"error": _sanitize_error(str(e))}
|
|
2185
|
+
|
|
2186
|
+
# ==================================================================
|
|
2187
|
+
# Tool 45: team_revoke_key
|
|
2188
|
+
# ==================================================================
|
|
2189
|
+
|
|
2190
|
+
def team_revoke_key(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2191
|
+
"""Revoke (delete) an API key from a team.
|
|
2192
|
+
|
|
2193
|
+
Args:
|
|
2194
|
+
params: Dict with keys: team_id (required, UUID),
|
|
2195
|
+
key_id (required, UUID).
|
|
2196
|
+
|
|
2197
|
+
Returns:
|
|
2198
|
+
Dict confirming the team key was revoked.
|
|
2199
|
+
"""
|
|
2200
|
+
team_id = params.get("team_id")
|
|
2201
|
+
key_id = params.get("key_id")
|
|
2202
|
+
|
|
2203
|
+
if team_id is None or key_id is None:
|
|
2204
|
+
return {"error": "team_id and key_id are required"}
|
|
2205
|
+
|
|
2206
|
+
err = _validate_uuid(team_id, "team_id")
|
|
2207
|
+
if err:
|
|
2208
|
+
return {"error": err}
|
|
2209
|
+
|
|
2210
|
+
err = _validate_uuid(key_id, "key_id")
|
|
2211
|
+
if err:
|
|
2212
|
+
return {"error": err}
|
|
2213
|
+
|
|
2214
|
+
try:
|
|
2215
|
+
result = self._request_with_retry(
|
|
2216
|
+
"DELETE",
|
|
2217
|
+
f"/api/teams/{quote(team_id, safe='')}/keys/{quote(key_id, safe='')}",
|
|
2218
|
+
)
|
|
2219
|
+
return result if isinstance(result, dict) else {"message": "Team API key revoked"}
|
|
2220
|
+
except Exception as e:
|
|
2221
|
+
return {"error": _sanitize_error(str(e))}
|
|
2222
|
+
|
|
2223
|
+
# ==================================================================
|
|
2224
|
+
# Tool 46: team_list_keys
|
|
2225
|
+
# ==================================================================
|
|
2226
|
+
|
|
2227
|
+
def team_list_keys(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2228
|
+
"""List all API keys for a team.
|
|
2229
|
+
|
|
2230
|
+
Args:
|
|
2231
|
+
params: Dict with key: team_id (required, UUID).
|
|
2232
|
+
|
|
2233
|
+
Returns:
|
|
2234
|
+
Dict with list of team API keys.
|
|
2235
|
+
"""
|
|
2236
|
+
team_id = params.get("team_id")
|
|
2237
|
+
err = _validate_uuid(team_id, "team_id")
|
|
2238
|
+
if err:
|
|
2239
|
+
return {"error": err}
|
|
2240
|
+
|
|
2241
|
+
try:
|
|
2242
|
+
result = self._request_with_retry(
|
|
2243
|
+
"GET",
|
|
2244
|
+
f"/api/teams/{quote(team_id, safe='')}/keys",
|
|
2245
|
+
)
|
|
2246
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
2247
|
+
except Exception as e:
|
|
2248
|
+
return {"error": _sanitize_error(str(e))}
|
|
2249
|
+
|
|
2250
|
+
# ==================================================================
|
|
2251
|
+
# Tool 47: team_list_members
|
|
2252
|
+
# ==================================================================
|
|
2253
|
+
|
|
2254
|
+
def team_list_members(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2255
|
+
"""List all members of a team.
|
|
2256
|
+
|
|
2257
|
+
Args:
|
|
2258
|
+
params: Dict with key: team_id (required, UUID).
|
|
2259
|
+
|
|
2260
|
+
Returns:
|
|
2261
|
+
Dict with list of team members.
|
|
2262
|
+
"""
|
|
2263
|
+
team_id = params.get("team_id")
|
|
2264
|
+
err = _validate_uuid(team_id, "team_id")
|
|
2265
|
+
if err:
|
|
2266
|
+
return {"error": err}
|
|
2267
|
+
|
|
2268
|
+
try:
|
|
2269
|
+
result = self._request_with_retry(
|
|
2270
|
+
"GET",
|
|
2271
|
+
f"/api/teams/{quote(team_id, safe='')}/members",
|
|
2272
|
+
)
|
|
2273
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
2274
|
+
except Exception as e:
|
|
2275
|
+
return {"error": _sanitize_error(str(e))}
|
|
2276
|
+
|
|
2277
|
+
# ==================================================================
|
|
2278
|
+
# Tool 48: team_add_member
|
|
2279
|
+
# ==================================================================
|
|
2280
|
+
|
|
2281
|
+
def team_add_member(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2282
|
+
"""Add a member to a team by email.
|
|
2283
|
+
|
|
2284
|
+
Args:
|
|
2285
|
+
params: Dict with keys: team_id (required, UUID),
|
|
2286
|
+
email (required), role (optional, 'member' or 'admin').
|
|
2287
|
+
|
|
2288
|
+
Returns:
|
|
2289
|
+
Dict with the added member details.
|
|
2290
|
+
"""
|
|
2291
|
+
team_id = params.get("team_id")
|
|
2292
|
+
email = params.get("email")
|
|
2293
|
+
role = params.get("role")
|
|
2294
|
+
|
|
2295
|
+
if team_id is None or email is None:
|
|
2296
|
+
return {"error": "team_id and email are required"}
|
|
2297
|
+
|
|
2298
|
+
err = _validate_uuid(team_id, "team_id")
|
|
2299
|
+
if err:
|
|
2300
|
+
return {"error": err}
|
|
2301
|
+
|
|
2302
|
+
if not email or not isinstance(email, str):
|
|
2303
|
+
return {"error": "email is required and must be a string"}
|
|
2304
|
+
if not _BASIC_EMAIL_RE.match(email):
|
|
2305
|
+
return {"error": "email must be a valid email address"}
|
|
2306
|
+
|
|
2307
|
+
if role is not None:
|
|
2308
|
+
if not isinstance(role, str) or role not in ("member", "admin"):
|
|
2309
|
+
return {"error": "role must be 'member' or 'admin'"}
|
|
2310
|
+
|
|
2311
|
+
body: Dict[str, Any] = {"email": email}
|
|
2312
|
+
if role is not None:
|
|
2313
|
+
body["role"] = role
|
|
2314
|
+
|
|
2315
|
+
try:
|
|
2316
|
+
result = self._request_with_retry(
|
|
2317
|
+
"POST",
|
|
2318
|
+
f"/api/teams/{quote(team_id, safe='')}/members",
|
|
2319
|
+
body,
|
|
2320
|
+
)
|
|
2321
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
2322
|
+
except Exception as e:
|
|
2323
|
+
return {"error": _sanitize_error(str(e))}
|
|
2324
|
+
|
|
2325
|
+
# ==================================================================
|
|
2326
|
+
# Tool 49: team_remove_member
|
|
2327
|
+
# ==================================================================
|
|
2328
|
+
|
|
2329
|
+
def team_remove_member(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2330
|
+
"""Remove a member from a team.
|
|
2331
|
+
|
|
2332
|
+
Args:
|
|
2333
|
+
params: Dict with keys: team_id (required, UUID),
|
|
2334
|
+
user_id (required, UUID).
|
|
2335
|
+
|
|
2336
|
+
Returns:
|
|
2337
|
+
Dict confirming the member was removed.
|
|
2338
|
+
"""
|
|
2339
|
+
team_id = params.get("team_id")
|
|
2340
|
+
user_id = params.get("user_id")
|
|
2341
|
+
|
|
2342
|
+
if team_id is None or user_id is None:
|
|
2343
|
+
return {"error": "team_id and user_id are required"}
|
|
2344
|
+
|
|
2345
|
+
err = _validate_uuid(team_id, "team_id")
|
|
2346
|
+
if err:
|
|
2347
|
+
return {"error": err}
|
|
2348
|
+
|
|
2349
|
+
err = _validate_uuid(user_id, "user_id")
|
|
2350
|
+
if err:
|
|
2351
|
+
return {"error": err}
|
|
2352
|
+
|
|
2353
|
+
try:
|
|
2354
|
+
result = self._request_with_retry(
|
|
2355
|
+
"DELETE",
|
|
2356
|
+
f"/api/teams/{quote(team_id, safe='')}/members/{quote(user_id, safe='')}",
|
|
2357
|
+
)
|
|
2358
|
+
return result if isinstance(result, dict) else {"message": "Team member removed"}
|
|
2359
|
+
except Exception as e:
|
|
2360
|
+
return {"error": _sanitize_error(str(e))}
|
|
2361
|
+
|
|
2362
|
+
# ==================================================================
|
|
2363
|
+
# Tool 50: team_invite_member
|
|
2364
|
+
# ==================================================================
|
|
2365
|
+
|
|
2366
|
+
def team_invite_member(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2367
|
+
"""Send an invitation to join a team.
|
|
2368
|
+
|
|
2369
|
+
Args:
|
|
2370
|
+
params: Dict with keys: team_id (required, UUID),
|
|
2371
|
+
email (required), role (optional, 'member' or 'admin').
|
|
2372
|
+
|
|
2373
|
+
Returns:
|
|
2374
|
+
Dict with the invitation details.
|
|
2375
|
+
"""
|
|
2376
|
+
team_id = params.get("team_id")
|
|
2377
|
+
email = params.get("email")
|
|
2378
|
+
role = params.get("role")
|
|
2379
|
+
|
|
2380
|
+
if team_id is None or email is None:
|
|
2381
|
+
return {"error": "team_id and email are required"}
|
|
2382
|
+
|
|
2383
|
+
err = _validate_uuid(team_id, "team_id")
|
|
2384
|
+
if err:
|
|
2385
|
+
return {"error": err}
|
|
2386
|
+
|
|
2387
|
+
if not email or not isinstance(email, str):
|
|
2388
|
+
return {"error": "email is required and must be a string"}
|
|
2389
|
+
if not _BASIC_EMAIL_RE.match(email):
|
|
2390
|
+
return {"error": "email must be a valid email address"}
|
|
2391
|
+
|
|
2392
|
+
if role is not None:
|
|
2393
|
+
if not isinstance(role, str) or role not in ("member", "admin"):
|
|
2394
|
+
return {"error": "role must be 'member' or 'admin'"}
|
|
2395
|
+
|
|
2396
|
+
body: Dict[str, Any] = {"email": email}
|
|
2397
|
+
if role is not None:
|
|
2398
|
+
body["role"] = role
|
|
2399
|
+
|
|
2400
|
+
try:
|
|
2401
|
+
result = self._request_with_retry(
|
|
2402
|
+
"POST",
|
|
2403
|
+
f"/api/teams/{quote(team_id, safe='')}/invites",
|
|
2404
|
+
body,
|
|
2405
|
+
)
|
|
2406
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
2407
|
+
except Exception as e:
|
|
2408
|
+
return {"error": _sanitize_error(str(e))}
|
|
2409
|
+
|
|
2410
|
+
# ==================================================================
|
|
2411
|
+
# Tool 51: team_list_invites
|
|
2412
|
+
# ==================================================================
|
|
2413
|
+
|
|
2414
|
+
def team_list_invites(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2415
|
+
"""List all pending invitations for a team.
|
|
2416
|
+
|
|
2417
|
+
Args:
|
|
2418
|
+
params: Dict with key: team_id (required, UUID).
|
|
2419
|
+
|
|
2420
|
+
Returns:
|
|
2421
|
+
Dict with list of team invitations.
|
|
2422
|
+
"""
|
|
2423
|
+
team_id = params.get("team_id")
|
|
2424
|
+
err = _validate_uuid(team_id, "team_id")
|
|
2425
|
+
if err:
|
|
2426
|
+
return {"error": err}
|
|
2427
|
+
|
|
2428
|
+
try:
|
|
2429
|
+
result = self._request_with_retry(
|
|
2430
|
+
"GET",
|
|
2431
|
+
f"/api/teams/{quote(team_id, safe='')}/invites",
|
|
2432
|
+
)
|
|
2433
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
2434
|
+
except Exception as e:
|
|
2435
|
+
return {"error": _sanitize_error(str(e))}
|
|
2436
|
+
|
|
2437
|
+
# ==================================================================
|
|
2438
|
+
# Tool 52: team_cancel_invite
|
|
2439
|
+
# ==================================================================
|
|
2440
|
+
|
|
2441
|
+
def team_cancel_invite(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
2442
|
+
"""Cancel a pending team invitation.
|
|
2443
|
+
|
|
2444
|
+
Args:
|
|
2445
|
+
params: Dict with keys: team_id (required, UUID),
|
|
2446
|
+
invite_id (required, UUID).
|
|
2447
|
+
|
|
2448
|
+
Returns:
|
|
2449
|
+
Dict confirming the invitation was cancelled.
|
|
2450
|
+
"""
|
|
2451
|
+
team_id = params.get("team_id")
|
|
2452
|
+
invite_id = params.get("invite_id")
|
|
2453
|
+
|
|
2454
|
+
if team_id is None or invite_id is None:
|
|
2455
|
+
return {"error": "team_id and invite_id are required"}
|
|
2456
|
+
|
|
2457
|
+
err = _validate_uuid(team_id, "team_id")
|
|
2458
|
+
if err:
|
|
2459
|
+
return {"error": err}
|
|
2460
|
+
|
|
2461
|
+
err = _validate_uuid(invite_id, "invite_id")
|
|
2462
|
+
if err:
|
|
2463
|
+
return {"error": err}
|
|
2464
|
+
|
|
2465
|
+
try:
|
|
2466
|
+
result = self._request_with_retry(
|
|
2467
|
+
"DELETE",
|
|
2468
|
+
f"/api/teams/{quote(team_id, safe='')}/invites/{quote(invite_id, safe='')}",
|
|
2469
|
+
)
|
|
2470
|
+
return result if isinstance(result, dict) else {"message": "Team invitation cancelled"}
|
|
2471
|
+
except Exception as e:
|
|
2472
|
+
return {"error": _sanitize_error(str(e))}
|
|
2473
|
+
|
|
1588
2474
|
# ==================================================================
|
|
1589
2475
|
# Tool dispatcher (Dify invokes tools by name)
|
|
1590
2476
|
# ==================================================================
|
|
@@ -1628,6 +2514,34 @@ class DominusNodeProvider:
|
|
|
1628
2514
|
"topup_paypal": self.topup_paypal,
|
|
1629
2515
|
"topup_stripe": self.topup_stripe,
|
|
1630
2516
|
"topup_crypto": self.topup_crypto,
|
|
2517
|
+
# --- Tools 26-52 (added to reach 53) ---
|
|
2518
|
+
"get_proxy_status": self.get_proxy_status,
|
|
2519
|
+
"get_transactions": self.get_transactions,
|
|
2520
|
+
"get_forecast": self.get_forecast,
|
|
2521
|
+
"check_payment": self.check_payment,
|
|
2522
|
+
"get_daily_usage": self.get_daily_usage,
|
|
2523
|
+
"get_top_hosts": self.get_top_hosts,
|
|
2524
|
+
"register": self.register,
|
|
2525
|
+
"login": self.login,
|
|
2526
|
+
"get_account_info": self.get_account_info,
|
|
2527
|
+
"verify_email": self.verify_email,
|
|
2528
|
+
"resend_verification": self.resend_verification,
|
|
2529
|
+
"update_password": self.update_password,
|
|
2530
|
+
"list_keys": self.list_keys,
|
|
2531
|
+
"create_key": self.create_key,
|
|
2532
|
+
"revoke_key": self.revoke_key,
|
|
2533
|
+
"get_plan": self.get_plan,
|
|
2534
|
+
"list_plans": self.list_plans,
|
|
2535
|
+
"change_plan": self.change_plan,
|
|
2536
|
+
"team_delete": self.team_delete,
|
|
2537
|
+
"team_revoke_key": self.team_revoke_key,
|
|
2538
|
+
"team_list_keys": self.team_list_keys,
|
|
2539
|
+
"team_list_members": self.team_list_members,
|
|
2540
|
+
"team_add_member": self.team_add_member,
|
|
2541
|
+
"team_remove_member": self.team_remove_member,
|
|
2542
|
+
"team_invite_member": self.team_invite_member,
|
|
2543
|
+
"team_list_invites": self.team_list_invites,
|
|
2544
|
+
"team_cancel_invite": self.team_cancel_invite,
|
|
1631
2545
|
}
|
|
1632
2546
|
|
|
1633
2547
|
handler = dispatch.get(tool_name)
|
|
@@ -1644,7 +2558,7 @@ class DominusNodeProvider:
|
|
|
1644
2558
|
|
|
1645
2559
|
@staticmethod
|
|
1646
2560
|
def get_tools() -> list[str]:
|
|
1647
|
-
"""Return the list of all
|
|
2561
|
+
"""Return the list of all 53 tool names.
|
|
1648
2562
|
|
|
1649
2563
|
Returns:
|
|
1650
2564
|
Sorted list of tool name strings.
|
|
@@ -1676,4 +2590,31 @@ class DominusNodeProvider:
|
|
|
1676
2590
|
"topup_paypal",
|
|
1677
2591
|
"topup_stripe",
|
|
1678
2592
|
"topup_crypto",
|
|
2593
|
+
"get_proxy_status",
|
|
2594
|
+
"get_transactions",
|
|
2595
|
+
"get_forecast",
|
|
2596
|
+
"check_payment",
|
|
2597
|
+
"get_daily_usage",
|
|
2598
|
+
"get_top_hosts",
|
|
2599
|
+
"register",
|
|
2600
|
+
"login",
|
|
2601
|
+
"get_account_info",
|
|
2602
|
+
"verify_email",
|
|
2603
|
+
"resend_verification",
|
|
2604
|
+
"update_password",
|
|
2605
|
+
"list_keys",
|
|
2606
|
+
"create_key",
|
|
2607
|
+
"revoke_key",
|
|
2608
|
+
"get_plan",
|
|
2609
|
+
"list_plans",
|
|
2610
|
+
"change_plan",
|
|
2611
|
+
"team_delete",
|
|
2612
|
+
"team_revoke_key",
|
|
2613
|
+
"team_list_keys",
|
|
2614
|
+
"team_list_members",
|
|
2615
|
+
"team_add_member",
|
|
2616
|
+
"team_remove_member",
|
|
2617
|
+
"team_invite_member",
|
|
2618
|
+
"team_list_invites",
|
|
2619
|
+
"team_cancel_invite",
|
|
1679
2620
|
])
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dominusnode-dify"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.2.0"
|
|
8
8
|
description = "Dominus Node tool provider for Dify -- 22 proxy, wallet, and team management tools"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominusnode_dify-1.1.0 → dominusnode_dify-1.2.0}/dominusnode_dify.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|