dominusnode-dify 1.0.1__tar.gz → 1.1.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.0.1 → dominusnode_dify-1.1.0}/PKG-INFO +1 -1
- {dominusnode_dify-1.0.1 → dominusnode_dify-1.1.0}/dominusnode_dify/__init__.py +3 -3
- {dominusnode_dify-1.0.1 → dominusnode_dify-1.1.0}/dominusnode_dify/tools.py +121 -12
- {dominusnode_dify-1.0.1 → dominusnode_dify-1.1.0}/dominusnode_dify.egg-info/PKG-INFO +1 -1
- {dominusnode_dify-1.0.1 → dominusnode_dify-1.1.0}/pyproject.toml +1 -1
- {dominusnode_dify-1.0.1 → dominusnode_dify-1.1.0}/tests/test_tools.py +1 -1
- {dominusnode_dify-1.0.1 → dominusnode_dify-1.1.0}/LICENSE +0 -0
- {dominusnode_dify-1.0.1 → dominusnode_dify-1.1.0}/README.md +0 -0
- {dominusnode_dify-1.0.1 → dominusnode_dify-1.1.0}/dominusnode_dify.egg-info/SOURCES.txt +0 -0
- {dominusnode_dify-1.0.1 → dominusnode_dify-1.1.0}/dominusnode_dify.egg-info/dependency_links.txt +0 -0
- {dominusnode_dify-1.0.1 → dominusnode_dify-1.1.0}/dominusnode_dify.egg-info/requires.txt +0 -0
- {dominusnode_dify-1.0.1 → dominusnode_dify-1.1.0}/dominusnode_dify.egg-info/top_level.txt +0 -0
- {dominusnode_dify-1.0.1 → dominusnode_dify-1.1.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"""Dominus Node Dify integration --
|
|
1
|
+
"""Dominus Node Dify integration -- 26 tools for the Dify AI platform.
|
|
2
2
|
|
|
3
|
-
Provides a Dify-compatible tool provider that exposes
|
|
3
|
+
Provides a Dify-compatible tool provider that exposes 26 tools for
|
|
4
4
|
interacting with the Dominus Node rotating proxy-as-a-service platform.
|
|
5
5
|
|
|
6
6
|
Tools cover:
|
|
@@ -10,7 +10,7 @@ Tools cover:
|
|
|
10
10
|
- Agentic wallet management (create, fund, freeze, unfreeze, delete)
|
|
11
11
|
- Team management (create, list, fund, keys, usage, update, role changes)
|
|
12
12
|
- x402 micropayment info
|
|
13
|
-
- PayPal top-up
|
|
13
|
+
- PayPal, Stripe, and crypto top-up
|
|
14
14
|
|
|
15
15
|
Security:
|
|
16
16
|
- Full SSRF prevention (private IP blocking, DNS rebinding, Teredo/6to4,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Dominus Node Dify tool provider --
|
|
1
|
+
"""Dominus Node Dify tool provider -- 26 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)
|
|
@@ -14,6 +14,7 @@ from __future__ import annotations
|
|
|
14
14
|
|
|
15
15
|
import ipaddress
|
|
16
16
|
import json
|
|
17
|
+
import math
|
|
17
18
|
import os
|
|
18
19
|
import re
|
|
19
20
|
import socket
|
|
@@ -476,7 +477,7 @@ def _validate_allowed_domains(domains: Any) -> Optional[str]:
|
|
|
476
477
|
class DominusNodeProvider:
|
|
477
478
|
"""Dify tool provider for Dominus Node rotating proxy service.
|
|
478
479
|
|
|
479
|
-
Authenticates using a Dominus Node API key and exposes
|
|
480
|
+
Authenticates using a Dominus Node API key and exposes 26 tool methods that
|
|
480
481
|
Dify can invoke. Each tool method receives ``credentials`` and ``params``
|
|
481
482
|
dicts from the Dify runtime and returns a dict result.
|
|
482
483
|
|
|
@@ -502,6 +503,7 @@ class DominusNodeProvider:
|
|
|
502
503
|
self.proxy_host: str = creds.get("proxy_host", "") or os.environ.get("DOMINUSNODE_PROXY_HOST", "proxy.dominusnode.com")
|
|
503
504
|
self.proxy_port: int = int(os.environ.get("DOMINUSNODE_PROXY_PORT", "8080"))
|
|
504
505
|
self.timeout: float = 30.0
|
|
506
|
+
self.agent_secret: str | None = creds.get("agent_secret") or os.environ.get("DOMINUSNODE_AGENT_SECRET")
|
|
505
507
|
self._token: str | None = None
|
|
506
508
|
|
|
507
509
|
# ------------------------------------------------------------------
|
|
@@ -523,6 +525,14 @@ class DominusNodeProvider:
|
|
|
523
525
|
"or DOMINUSNODE_API_KEY environment variable."
|
|
524
526
|
)
|
|
525
527
|
|
|
528
|
+
auth_headers = {
|
|
529
|
+
"User-Agent": "dominusnode-dify/1.0.0",
|
|
530
|
+
"Content-Type": "application/json",
|
|
531
|
+
}
|
|
532
|
+
if self.agent_secret:
|
|
533
|
+
auth_headers["X-DominusNode-Agent"] = "mcp"
|
|
534
|
+
auth_headers["X-DominusNode-Agent-Secret"] = self.agent_secret
|
|
535
|
+
|
|
526
536
|
with httpx.Client(
|
|
527
537
|
timeout=self.timeout,
|
|
528
538
|
follow_redirects=False,
|
|
@@ -530,10 +540,7 @@ class DominusNodeProvider:
|
|
|
530
540
|
resp = client.post(
|
|
531
541
|
f"{self.base_url}/api/auth/verify-key",
|
|
532
542
|
json={"apiKey": self.api_key},
|
|
533
|
-
headers=
|
|
534
|
-
"User-Agent": "dominusnode-dify/1.0.0",
|
|
535
|
-
"Content-Type": "application/json",
|
|
536
|
-
},
|
|
543
|
+
headers=auth_headers,
|
|
537
544
|
)
|
|
538
545
|
|
|
539
546
|
if resp.status_code != 200:
|
|
@@ -584,12 +591,17 @@ class DominusNodeProvider:
|
|
|
584
591
|
timeout=self.timeout,
|
|
585
592
|
follow_redirects=False,
|
|
586
593
|
) as client:
|
|
594
|
+
req_headers = {
|
|
595
|
+
"User-Agent": "dominusnode-dify/1.0.0",
|
|
596
|
+
"Content-Type": "application/json",
|
|
597
|
+
"Authorization": f"Bearer {self._token}",
|
|
598
|
+
}
|
|
599
|
+
if self.agent_secret:
|
|
600
|
+
req_headers["X-DominusNode-Agent"] = "mcp"
|
|
601
|
+
req_headers["X-DominusNode-Agent-Secret"] = self.agent_secret
|
|
602
|
+
|
|
587
603
|
kwargs: Dict[str, Any] = {
|
|
588
|
-
"headers":
|
|
589
|
-
"User-Agent": "dominusnode-dify/1.0.0",
|
|
590
|
-
"Content-Type": "application/json",
|
|
591
|
-
"Authorization": f"Bearer {self._token}",
|
|
592
|
-
},
|
|
604
|
+
"headers": req_headers,
|
|
593
605
|
}
|
|
594
606
|
|
|
595
607
|
if body is not None and method.upper() not in ("GET", "HEAD", "OPTIONS"):
|
|
@@ -1480,6 +1492,99 @@ class DominusNodeProvider:
|
|
|
1480
1492
|
except Exception as e:
|
|
1481
1493
|
return {"error": _sanitize_error(str(e))}
|
|
1482
1494
|
|
|
1495
|
+
# ==================================================================
|
|
1496
|
+
# Tool 24: topup_stripe
|
|
1497
|
+
# ==================================================================
|
|
1498
|
+
|
|
1499
|
+
def topup_stripe(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
1500
|
+
"""Create a Stripe checkout session for wallet top-up.
|
|
1501
|
+
|
|
1502
|
+
Args:
|
|
1503
|
+
params: Dict with key: amount_cents (required, 500-100000).
|
|
1504
|
+
|
|
1505
|
+
Returns:
|
|
1506
|
+
Dict with checkout session details.
|
|
1507
|
+
"""
|
|
1508
|
+
amount_cents = params.get("amount_cents")
|
|
1509
|
+
if amount_cents is None:
|
|
1510
|
+
return {"error": "amount_cents is required"}
|
|
1511
|
+
|
|
1512
|
+
# Dify sends numbers as float; coerce to int
|
|
1513
|
+
if isinstance(amount_cents, float):
|
|
1514
|
+
if not math.isfinite(amount_cents):
|
|
1515
|
+
return {"error": "amount_cents must be an integer between 500 and 100000"}
|
|
1516
|
+
if amount_cents == int(amount_cents):
|
|
1517
|
+
amount_cents = int(amount_cents)
|
|
1518
|
+
|
|
1519
|
+
err = _validate_positive_int(
|
|
1520
|
+
amount_cents, "amount_cents", min_value=500, max_value=100_000
|
|
1521
|
+
)
|
|
1522
|
+
if err:
|
|
1523
|
+
return {"error": err}
|
|
1524
|
+
|
|
1525
|
+
try:
|
|
1526
|
+
result = self._request_with_retry(
|
|
1527
|
+
"POST",
|
|
1528
|
+
"/api/wallet/topup/stripe",
|
|
1529
|
+
{"amountCents": amount_cents},
|
|
1530
|
+
)
|
|
1531
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
1532
|
+
except Exception as e:
|
|
1533
|
+
return {"error": _sanitize_error(str(e))}
|
|
1534
|
+
|
|
1535
|
+
# ==================================================================
|
|
1536
|
+
# Tool 25: topup_crypto
|
|
1537
|
+
# ==================================================================
|
|
1538
|
+
|
|
1539
|
+
_VALID_CRYPTO_CURRENCIES = {
|
|
1540
|
+
"BTC", "ETH", "LTC", "XMR", "ZEC",
|
|
1541
|
+
"USDC", "SOL", "USDT", "DAI", "BNB", "LINK",
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
def topup_crypto(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
1545
|
+
"""Create a crypto payment invoice for wallet top-up.
|
|
1546
|
+
|
|
1547
|
+
Args:
|
|
1548
|
+
params: Dict with keys: amount_usd (required, 5-1000),
|
|
1549
|
+
currency (required, one of BTC/ETH/LTC/XMR/ZEC/USDC/SOL/
|
|
1550
|
+
USDT/DAI/BNB/LINK).
|
|
1551
|
+
|
|
1552
|
+
Returns:
|
|
1553
|
+
Dict with invoice details for completing the crypto payment.
|
|
1554
|
+
"""
|
|
1555
|
+
amount_usd = params.get("amount_usd")
|
|
1556
|
+
currency = params.get("currency")
|
|
1557
|
+
|
|
1558
|
+
if amount_usd is None:
|
|
1559
|
+
return {"error": "amount_usd is required"}
|
|
1560
|
+
|
|
1561
|
+
# Dify sends numbers as float; keep as-is but validate
|
|
1562
|
+
if isinstance(amount_usd, bool) or not isinstance(amount_usd, (int, float)):
|
|
1563
|
+
return {"error": "amount_usd must be a number between 5 and 1,000"}
|
|
1564
|
+
if not math.isfinite(amount_usd):
|
|
1565
|
+
return {"error": "amount_usd must be a number between 5 and 1,000"}
|
|
1566
|
+
if amount_usd < 5 or amount_usd > 1000:
|
|
1567
|
+
return {"error": "amount_usd must be a number between 5 and 1,000"}
|
|
1568
|
+
|
|
1569
|
+
if not currency or not isinstance(currency, str):
|
|
1570
|
+
return {
|
|
1571
|
+
"error": "currency must be one of: BTC, ETH, LTC, XMR, ZEC, USDC, SOL, USDT, DAI, BNB, LINK",
|
|
1572
|
+
}
|
|
1573
|
+
if currency.upper() not in self._VALID_CRYPTO_CURRENCIES:
|
|
1574
|
+
return {
|
|
1575
|
+
"error": "currency must be one of: BTC, ETH, LTC, XMR, ZEC, USDC, SOL, USDT, DAI, BNB, LINK",
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
try:
|
|
1579
|
+
result = self._request_with_retry(
|
|
1580
|
+
"POST",
|
|
1581
|
+
"/api/wallet/topup/crypto",
|
|
1582
|
+
{"amountUsd": amount_usd, "currency": currency.lower()},
|
|
1583
|
+
)
|
|
1584
|
+
return result if isinstance(result, dict) else {"data": result}
|
|
1585
|
+
except Exception as e:
|
|
1586
|
+
return {"error": _sanitize_error(str(e))}
|
|
1587
|
+
|
|
1483
1588
|
# ==================================================================
|
|
1484
1589
|
# Tool dispatcher (Dify invokes tools by name)
|
|
1485
1590
|
# ==================================================================
|
|
@@ -1521,6 +1626,8 @@ class DominusNodeProvider:
|
|
|
1521
1626
|
"update_team_member_role": self.update_team_member_role,
|
|
1522
1627
|
"x402_info": self.x402_info,
|
|
1523
1628
|
"topup_paypal": self.topup_paypal,
|
|
1629
|
+
"topup_stripe": self.topup_stripe,
|
|
1630
|
+
"topup_crypto": self.topup_crypto,
|
|
1524
1631
|
}
|
|
1525
1632
|
|
|
1526
1633
|
handler = dispatch.get(tool_name)
|
|
@@ -1537,7 +1644,7 @@ class DominusNodeProvider:
|
|
|
1537
1644
|
|
|
1538
1645
|
@staticmethod
|
|
1539
1646
|
def get_tools() -> list[str]:
|
|
1540
|
-
"""Return the list of all
|
|
1647
|
+
"""Return the list of all 26 tool names.
|
|
1541
1648
|
|
|
1542
1649
|
Returns:
|
|
1543
1650
|
Sorted list of tool name strings.
|
|
@@ -1567,4 +1674,6 @@ class DominusNodeProvider:
|
|
|
1567
1674
|
"update_team_member_role",
|
|
1568
1675
|
"x402_info",
|
|
1569
1676
|
"topup_paypal",
|
|
1677
|
+
"topup_stripe",
|
|
1678
|
+
"topup_crypto",
|
|
1570
1679
|
])
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dominusnode-dify"
|
|
7
|
-
version = "1.0
|
|
7
|
+
version = "1.1.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"}
|
|
@@ -453,7 +453,7 @@ class TestProviderInit:
|
|
|
453
453
|
|
|
454
454
|
def test_get_tools_returns_24(self):
|
|
455
455
|
tools = DominusNodeProvider.get_tools()
|
|
456
|
-
assert len(tools) ==
|
|
456
|
+
assert len(tools) == 26
|
|
457
457
|
assert "proxied_fetch" in tools
|
|
458
458
|
assert "check_balance" in tools
|
|
459
459
|
assert "topup_paypal" in tools
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominusnode_dify-1.0.1 → dominusnode_dify-1.1.0}/dominusnode_dify.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|