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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dominusnode-dify
3
- Version: 1.0.1
3
+ Version: 1.1.0
4
4
  Summary: Dominus Node tool provider for Dify -- 22 proxy, wallet, and team management tools
5
5
  License: MIT
6
6
  Requires-Python: >=3.10
@@ -1,6 +1,6 @@
1
- """Dominus Node Dify integration -- 24 tools for the Dify AI platform.
1
+ """Dominus Node Dify integration -- 26 tools for the Dify AI platform.
2
2
 
3
- Provides a Dify-compatible tool provider that exposes 24 tools for
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 -- 24 tools with full security hardening.
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 24 tool methods that
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 22 tool names (plus topup_paypal = 23 entries).
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
  ])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dominusnode-dify
3
- Version: 1.0.1
3
+ Version: 1.1.0
4
4
  Summary: Dominus Node tool provider for Dify -- 22 proxy, wallet, and team management tools
5
5
  License: MIT
6
6
  Requires-Python: >=3.10
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dominusnode-dify"
7
- version = "1.0.1"
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) == 24
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