@dominusnode/openclaw-plugin 1.0.1 → 1.2.0

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.
package/dist/plugin.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Dominus Node OpenClaw Plugin
3
3
  *
4
- * Implements 24 tools for interacting with Dominus Node's rotating proxy service
4
+ * Implements 53 tools for interacting with Dominus Node's rotating proxy service
5
5
  * directly from OpenClaw AI coding sessions.
6
6
  *
7
7
  * Uses native fetch (no external dependencies). Runs via jiti runtime.
package/dist/plugin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Dominus Node OpenClaw Plugin
3
3
  *
4
- * Implements 24 tools for interacting with Dominus Node's rotating proxy service
4
+ * Implements 53 tools for interacting with Dominus Node's rotating proxy service
5
5
  * directly from OpenClaw AI coding sessions.
6
6
  *
7
7
  * Uses native fetch (no external dependencies). Runs via jiti runtime.
@@ -61,6 +61,9 @@ function getProxyPort() {
61
61
  return 8080;
62
62
  return port;
63
63
  }
64
+ function getAgentSecret() {
65
+ return process.env.DOMINUSNODE_AGENT_SECRET || undefined;
66
+ }
64
67
  // ---------------------------------------------------------------------------
65
68
  // Credential scrubbing
66
69
  // ---------------------------------------------------------------------------
@@ -351,9 +354,18 @@ async function ensureAuth(apiKey, baseUrl) {
351
354
  const keyPrefix = apiKey.slice(0, 16);
352
355
  if (cachedJwt && Date.now() < jwtExpiresAt && cachedApiKeyPrefix === keyPrefix)
353
356
  return cachedJwt;
357
+ const authHeaders = {
358
+ "Content-Type": "application/json",
359
+ "User-Agent": "dominusnode-openclaw-plugin/1.0.0",
360
+ };
361
+ const agentSecret = getAgentSecret();
362
+ if (agentSecret) {
363
+ authHeaders["X-DominusNode-Agent"] = "mcp";
364
+ authHeaders["X-DominusNode-Agent-Secret"] = agentSecret;
365
+ }
354
366
  const res = await fetch(`${baseUrl}/api/auth/verify-key`, {
355
367
  method: "POST",
356
- headers: { "Content-Type": "application/json", "User-Agent": "dominusnode-openclaw-plugin/1.0.0" },
368
+ headers: authHeaders,
357
369
  body: JSON.stringify({ apiKey }),
358
370
  redirect: "error",
359
371
  });
@@ -378,6 +390,11 @@ async function apiRequest(method, path, body) {
378
390
  "Accept": "application/json",
379
391
  "User-Agent": "dominusnode-openclaw-plugin/1.0.0",
380
392
  };
393
+ const agentSecret = getAgentSecret();
394
+ if (agentSecret) {
395
+ headers["X-DominusNode-Agent"] = "mcp";
396
+ headers["X-DominusNode-Agent-Secret"] = agentSecret;
397
+ }
381
398
  const init = {
382
399
  method,
383
400
  headers,
@@ -449,6 +466,83 @@ async function apiDelete(path) {
449
466
  async function apiPatch(path, body) {
450
467
  return apiRequest("PATCH", path, body);
451
468
  }
469
+ async function apiPut(path, body) {
470
+ return apiRequest("PUT", path, body);
471
+ }
472
+ /**
473
+ * Make an unauthenticated API request (for register, login, verify-email).
474
+ * Does NOT call ensureAuth(). Includes agent headers if available.
475
+ */
476
+ async function unauthenticatedRequest(method, path, body) {
477
+ const baseUrl = getBaseUrl();
478
+ const url = `${baseUrl}${path}`;
479
+ const headers = {
480
+ "Accept": "application/json",
481
+ "User-Agent": "dominusnode-openclaw-plugin/1.0.0",
482
+ };
483
+ const agentSecret = getAgentSecret();
484
+ if (agentSecret) {
485
+ headers["X-DominusNode-Agent"] = "mcp";
486
+ headers["X-DominusNode-Agent-Secret"] = agentSecret;
487
+ }
488
+ const init = {
489
+ method,
490
+ headers,
491
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
492
+ redirect: "error",
493
+ };
494
+ if (body && (method === "POST" || method === "PUT" || method === "PATCH")) {
495
+ headers["Content-Type"] = "application/json";
496
+ init.body = JSON.stringify(body);
497
+ }
498
+ let res;
499
+ try {
500
+ res = await fetch(url, init);
501
+ }
502
+ catch (err) {
503
+ throw new Error(`API request failed: ${safeError(err)}`);
504
+ }
505
+ let rawText;
506
+ try {
507
+ rawText = await res.text();
508
+ }
509
+ catch {
510
+ rawText = "";
511
+ }
512
+ if (new TextEncoder().encode(rawText).length > MAX_RESPONSE_BYTES) {
513
+ throw new Error("API response too large");
514
+ }
515
+ if (!res.ok) {
516
+ let errorMsg = `API error ${res.status}`;
517
+ if (rawText) {
518
+ try {
519
+ const parsed = JSON.parse(rawText);
520
+ stripDangerousKeys(parsed);
521
+ if (parsed.error) {
522
+ errorMsg = `API error ${res.status}: ${scrubCredentials(String(parsed.error))}`;
523
+ }
524
+ else if (parsed.message) {
525
+ errorMsg = `API error ${res.status}: ${scrubCredentials(String(parsed.message))}`;
526
+ }
527
+ }
528
+ catch {
529
+ errorMsg = `API error ${res.status}: ${scrubCredentials(rawText.slice(0, 200))}`;
530
+ }
531
+ }
532
+ throw new Error(errorMsg);
533
+ }
534
+ if (!rawText || rawText.trim().length === 0) {
535
+ return {};
536
+ }
537
+ try {
538
+ const parsed = JSON.parse(rawText);
539
+ stripDangerousKeys(parsed);
540
+ return parsed;
541
+ }
542
+ catch {
543
+ throw new Error("Failed to parse API response as JSON");
544
+ }
545
+ }
452
546
  // ---------------------------------------------------------------------------
453
547
  // Formatting helpers
454
548
  // ---------------------------------------------------------------------------
@@ -1454,7 +1548,92 @@ const topupPaypalTool = {
1454
1548
  }
1455
1549
  },
1456
1550
  };
1457
- // 22. x402_info
1551
+ // 22. topup_stripe
1552
+ const topupStripeTool = {
1553
+ name: "topup_stripe",
1554
+ description: "Top up your Dominus Node wallet balance via Stripe. Creates a Stripe checkout session and returns " +
1555
+ "a checkout URL to complete payment. Minimum $5 (500 cents), maximum $1,000 (100000 cents).",
1556
+ parameters: {
1557
+ amount_cents: {
1558
+ type: "number",
1559
+ description: "Amount in cents to top up (min 500 = $5, max 100000 = $1,000)",
1560
+ required: true,
1561
+ },
1562
+ },
1563
+ execute: async (args) => {
1564
+ try {
1565
+ const amountCents = Number(args.amount_cents ?? 0);
1566
+ if (!Number.isInteger(amountCents) || amountCents < 500 || amountCents > 100000) {
1567
+ return "Error: amount_cents must be an integer between 500 ($5) and 100000 ($1,000).";
1568
+ }
1569
+ const data = await apiPost("/api/wallet/topup/stripe", { amountCents });
1570
+ return [
1571
+ "Stripe Checkout Session Created",
1572
+ "",
1573
+ `Session ID: ${data.sessionId}`,
1574
+ `Amount: ${formatCents(amountCents)}`,
1575
+ `Checkout URL: ${data.url}`,
1576
+ "",
1577
+ "Open the checkout URL in a browser to complete payment.",
1578
+ "Once paid, the funds will be credited to your wallet automatically.",
1579
+ ].join("\n");
1580
+ }
1581
+ catch (err) {
1582
+ return `Error: ${safeError(err)}`;
1583
+ }
1584
+ },
1585
+ };
1586
+ // 23. topup_crypto
1587
+ const VALID_CRYPTO_CURRENCIES = new Set([
1588
+ "BTC", "ETH", "LTC", "XMR", "ZEC", "USDC", "SOL", "USDT", "DAI", "BNB", "LINK",
1589
+ ]);
1590
+ const topupCryptoTool = {
1591
+ name: "topup_crypto",
1592
+ description: "Top up your Dominus Node wallet balance via cryptocurrency. Creates a crypto payment invoice " +
1593
+ "and returns payment details. Minimum $5, maximum $1,000. " +
1594
+ "Supported currencies: BTC, ETH, LTC, XMR, ZEC, USDC, SOL, USDT, DAI, BNB, LINK.",
1595
+ parameters: {
1596
+ amount_usd: {
1597
+ type: "number",
1598
+ description: "Amount in USD to top up (min 5, max 1000)",
1599
+ required: true,
1600
+ },
1601
+ currency: {
1602
+ type: "string",
1603
+ description: "Cryptocurrency to pay with",
1604
+ required: true,
1605
+ enum: ["BTC", "ETH", "LTC", "XMR", "ZEC", "USDC", "SOL", "USDT", "DAI", "BNB", "LINK"],
1606
+ },
1607
+ },
1608
+ execute: async (args) => {
1609
+ try {
1610
+ const amountUsd = Number(args.amount_usd ?? 0);
1611
+ if (typeof amountUsd !== "number" || !Number.isFinite(amountUsd) || amountUsd < 5 || amountUsd > 1000) {
1612
+ return "Error: amount_usd must be a number between 5 and 1000.";
1613
+ }
1614
+ const currency = String(args.currency ?? "").toUpperCase();
1615
+ if (!VALID_CRYPTO_CURRENCIES.has(currency)) {
1616
+ return `Error: currency must be one of: ${[...VALID_CRYPTO_CURRENCIES].join(", ")}.`;
1617
+ }
1618
+ const data = await apiPost("/api/wallet/topup/crypto", { amountUsd, currency: currency.toLowerCase() });
1619
+ return [
1620
+ "Crypto Payment Invoice Created",
1621
+ "",
1622
+ `Invoice ID: ${data.invoiceId}`,
1623
+ `Amount: $${amountUsd}`,
1624
+ `Pay: ${data.priceAmount} ${data.payCurrency?.toUpperCase() ?? currency}`,
1625
+ `Payment URL: ${data.invoiceUrl}`,
1626
+ "",
1627
+ "Open the payment URL to complete payment.",
1628
+ "Once confirmed on-chain, the funds will be credited to your wallet.",
1629
+ ].join("\n");
1630
+ }
1631
+ catch (err) {
1632
+ return `Error: ${safeError(err)}`;
1633
+ }
1634
+ },
1635
+ };
1636
+ // 24. x402_info
1458
1637
  const x402InfoTool = {
1459
1638
  name: "x402_info",
1460
1639
  description: "Get x402 micropayment protocol information including supported " +
@@ -1470,7 +1649,7 @@ const x402InfoTool = {
1470
1649
  }
1471
1650
  },
1472
1651
  };
1473
- // 23. update_team_member_role
1652
+ // 25. update_team_member_role
1474
1653
  const updateTeamMemberRoleTool = {
1475
1654
  name: "update_team_member_role",
1476
1655
  description: "Update a team member's role (member or admin). Only owners and admins can change roles.",
@@ -1516,7 +1695,7 @@ const updateTeamMemberRoleTool = {
1516
1695
  }
1517
1696
  },
1518
1697
  };
1519
- // 24. update_wallet_policy
1698
+ // 26. update_wallet_policy
1520
1699
  const updateWalletPolicyTool = {
1521
1700
  name: "update_wallet_policy",
1522
1701
  description: "Update the policy of an agentic wallet. Sets or clears the daily budget cap " +
@@ -1597,34 +1776,1030 @@ const updateWalletPolicyTool = {
1597
1776
  }
1598
1777
  },
1599
1778
  };
1600
- // ---------------------------------------------------------------------------
1601
- // Plugin export — the tools array that OpenClaw discovers
1602
- // ---------------------------------------------------------------------------
1603
- export const tools = [
1604
- proxiedFetchTool,
1605
- checkBalanceTool,
1606
- checkUsageTool,
1607
- getProxyConfigTool,
1608
- listSessionsTool,
1609
- createAgenticWalletTool,
1610
- fundAgenticWalletTool,
1611
- checkAgenticBalanceTool,
1612
- listAgenticWalletsTool,
1613
- agenticTransactionsTool,
1614
- freezeAgenticWalletTool,
1615
- unfreezeAgenticWalletTool,
1616
- deleteAgenticWalletTool,
1617
- createTeamTool,
1618
- listTeamsTool,
1619
- teamDetailsTool,
1620
- teamFundTool,
1621
- teamCreateKeyTool,
1622
- teamUsageTool,
1623
- updateTeamTool,
1624
- updateTeamMemberRoleTool,
1625
- topupPaypalTool,
1626
- x402InfoTool,
1627
- updateWalletPolicyTool,
1779
+ // 27. get_proxy_status
1780
+ const getProxyStatusTool = {
1781
+ name: "get_proxy_status",
1782
+ description: "Get the current status of the proxy gateway including uptime, active connections, and pool health.",
1783
+ parameters: {},
1784
+ execute: async () => {
1785
+ try {
1786
+ const data = await apiGet("/api/proxy/status");
1787
+ const lines = [
1788
+ "Proxy Gateway Status",
1789
+ "",
1790
+ `Status: ${data.status ?? "unknown"}`,
1791
+ `Uptime: ${data.uptime != null ? `${Math.floor(data.uptime / 3600)}h ${Math.floor((data.uptime % 3600) / 60)}m` : "unknown"}`,
1792
+ `Active Connections: ${data.activeConnections ?? 0}`,
1793
+ ];
1794
+ const pools = data.pools ?? [];
1795
+ if (pools.length > 0) {
1796
+ lines.push("", "Pool Health:");
1797
+ for (const p of pools) {
1798
+ lines.push(` ${p.name}: ${p.healthy ? "healthy" : "unhealthy"} (${p.activeIps} IPs)`);
1799
+ }
1800
+ }
1801
+ return lines.join("\n");
1802
+ }
1803
+ catch (err) {
1804
+ return `Error: ${safeError(err)}`;
1805
+ }
1806
+ },
1807
+ };
1808
+ // 28. get_transactions
1809
+ const getTransactionsTool = {
1810
+ name: "get_transactions",
1811
+ description: "Get your main wallet transaction history including top-ups, usage charges, and refunds.",
1812
+ parameters: {
1813
+ limit: {
1814
+ type: "number",
1815
+ description: "Number of transactions to return (1-100, default 20)",
1816
+ required: false,
1817
+ default: 20,
1818
+ },
1819
+ },
1820
+ execute: async (args) => {
1821
+ try {
1822
+ const limit = Math.min(Math.max(Number(args.limit ?? 20), 1), 100);
1823
+ const data = await apiGet(`/api/wallet/transactions?limit=${limit}`);
1824
+ const txs = data.transactions ?? [];
1825
+ if (txs.length === 0) {
1826
+ return "No wallet transactions found.";
1827
+ }
1828
+ const lines = [`Wallet Transactions (${txs.length})`, ""];
1829
+ for (const tx of txs) {
1830
+ const sign = tx.type === "topup" || tx.type === "refund" || tx.type === "fund" ? "+" : "-";
1831
+ lines.push(` ${sign}${formatCents(Math.abs(tx.amountCents))} [${tx.type}] ${tx.description}`);
1832
+ lines.push(` ${tx.createdAt}`);
1833
+ }
1834
+ return truncate(lines.join("\n"));
1835
+ }
1836
+ catch (err) {
1837
+ return `Error: ${safeError(err)}`;
1838
+ }
1839
+ },
1840
+ };
1841
+ // 29. get_forecast
1842
+ const getForecastTool = {
1843
+ name: "get_forecast",
1844
+ description: "Get a spending forecast based on recent usage patterns. Shows projected balance depletion date and daily burn rate.",
1845
+ parameters: {},
1846
+ execute: async () => {
1847
+ try {
1848
+ const data = await apiGet("/api/wallet/forecast");
1849
+ const lines = [
1850
+ "Spending Forecast",
1851
+ "",
1852
+ `Current Balance: ${formatCents(data.currentBalanceCents ?? 0)}`,
1853
+ `Daily Burn Rate: ${formatCents(data.dailyBurnCents ?? 0)}/day`,
1854
+ `Avg Daily Bandwidth: ${formatBytes(data.avgDailyBytes ?? 0)}`,
1855
+ `Estimated Days Remaining: ${data.estimatedDaysRemaining ?? "N/A"}`,
1856
+ `Projected Depletion: ${data.projectedDepletionDate ?? "N/A"}`,
1857
+ "",
1858
+ "Use topup_stripe, topup_paypal, or topup_crypto to add funds.",
1859
+ ];
1860
+ return lines.join("\n");
1861
+ }
1862
+ catch (err) {
1863
+ return `Error: ${safeError(err)}`;
1864
+ }
1865
+ },
1866
+ };
1867
+ // 30. check_payment
1868
+ const checkPaymentTool = {
1869
+ name: "check_payment",
1870
+ description: "Check the status of a crypto payment invoice. Use the invoice ID returned by topup_crypto.",
1871
+ parameters: {
1872
+ invoice_id: {
1873
+ type: "string",
1874
+ description: "Crypto invoice ID to check",
1875
+ required: true,
1876
+ },
1877
+ },
1878
+ execute: async (args) => {
1879
+ try {
1880
+ const invoiceId = String(args.invoice_id ?? "").trim();
1881
+ if (!invoiceId || invoiceId.length === 0 || invoiceId.length > 200) {
1882
+ return "Error: invoice_id is required (max 200 characters).";
1883
+ }
1884
+ if (/[\x00-\x1f\x7f]/.test(invoiceId)) {
1885
+ return "Error: invoice_id contains invalid control characters.";
1886
+ }
1887
+ const data = await apiGet(`/api/wallet/topup/crypto/${encodeURIComponent(invoiceId)}/status`);
1888
+ const lines = [
1889
+ "Crypto Payment Status",
1890
+ "",
1891
+ `Invoice ID: ${data.invoiceId ?? invoiceId}`,
1892
+ `Status: ${data.status}`,
1893
+ `Amount: $${data.amountUsd}`,
1894
+ `Currency: ${data.payCurrency?.toUpperCase() ?? "N/A"}`,
1895
+ ];
1896
+ if (data.paidAmount != null) {
1897
+ lines.push(`Paid: ${data.paidAmount} ${data.payCurrency?.toUpperCase() ?? ""}`);
1898
+ }
1899
+ lines.push(`Created: ${data.createdAt}`, `Updated: ${data.updatedAt}`);
1900
+ return lines.join("\n");
1901
+ }
1902
+ catch (err) {
1903
+ return `Error: ${safeError(err)}`;
1904
+ }
1905
+ },
1906
+ };
1907
+ // 31. get_daily_usage
1908
+ const getDailyUsageTool = {
1909
+ name: "get_daily_usage",
1910
+ description: "Get daily bandwidth usage breakdown for the specified number of days. Shows per-day bytes, cost, and request count.",
1911
+ parameters: {
1912
+ days: {
1913
+ type: "number",
1914
+ description: "Number of days to look back (1-90, default 7)",
1915
+ required: false,
1916
+ default: 7,
1917
+ },
1918
+ },
1919
+ execute: async (args) => {
1920
+ try {
1921
+ const days = Math.min(Math.max(Number(args.days ?? 7), 1), 90);
1922
+ const data = await apiGet(`/api/usage/daily?days=${days}`);
1923
+ const daily = data.daily ?? [];
1924
+ if (daily.length === 0) {
1925
+ return `No daily usage data found for the last ${days} days.`;
1926
+ }
1927
+ const lines = [`Daily Usage (last ${days} days)`, ""];
1928
+ for (const d of daily) {
1929
+ lines.push(` ${d.date}: ${formatBytes(d.totalBytes)} | ${formatCents(d.totalCostCents)} | ${d.requestCount} reqs`);
1930
+ }
1931
+ return truncate(lines.join("\n"));
1932
+ }
1933
+ catch (err) {
1934
+ return `Error: ${safeError(err)}`;
1935
+ }
1936
+ },
1937
+ };
1938
+ // 32. get_top_hosts
1939
+ const getTopHostsTool = {
1940
+ name: "get_top_hosts",
1941
+ description: "Get the top hosts by bandwidth usage. Shows which domains consume the most proxy bandwidth.",
1942
+ parameters: {
1943
+ limit: {
1944
+ type: "number",
1945
+ description: "Number of top hosts to return (1-100, default 10)",
1946
+ required: false,
1947
+ default: 10,
1948
+ },
1949
+ },
1950
+ execute: async (args) => {
1951
+ try {
1952
+ const limit = Math.min(Math.max(Number(args.limit ?? 10), 1), 100);
1953
+ const data = await apiGet(`/api/usage/top-hosts?limit=${limit}`);
1954
+ const hosts = data.hosts ?? [];
1955
+ if (hosts.length === 0) {
1956
+ return "No host usage data found.";
1957
+ }
1958
+ const lines = [`Top Hosts by Bandwidth (${hosts.length})`, ""];
1959
+ for (let i = 0; i < hosts.length; i++) {
1960
+ const h = hosts[i];
1961
+ lines.push(` ${i + 1}. ${h.host}: ${formatBytes(h.totalBytes)} | ${h.requestCount} reqs | Last: ${h.lastSeen}`);
1962
+ }
1963
+ return truncate(lines.join("\n"));
1964
+ }
1965
+ catch (err) {
1966
+ return `Error: ${safeError(err)}`;
1967
+ }
1968
+ },
1969
+ };
1970
+ // 33. register
1971
+ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1972
+ const registerTool = {
1973
+ name: "register",
1974
+ description: "Register a new Dominus Node account. Returns user info and instructions for email verification. " +
1975
+ "This is an unauthenticated endpoint -- no API key required.",
1976
+ parameters: {
1977
+ email: {
1978
+ type: "string",
1979
+ description: "Email address for the new account",
1980
+ required: true,
1981
+ },
1982
+ password: {
1983
+ type: "string",
1984
+ description: "Password for the new account (8-128 characters)",
1985
+ required: true,
1986
+ },
1987
+ },
1988
+ execute: async (args) => {
1989
+ try {
1990
+ const email = String(args.email ?? "").trim();
1991
+ if (!email || !EMAIL_RE.test(email)) {
1992
+ return "Error: A valid email address is required.";
1993
+ }
1994
+ if (email.length > 254) {
1995
+ return "Error: Email address is too long (max 254 characters).";
1996
+ }
1997
+ const password = String(args.password ?? "");
1998
+ if (password.length < 8 || password.length > 128) {
1999
+ return "Error: Password must be 8-128 characters.";
2000
+ }
2001
+ const data = await unauthenticatedRequest("POST", "/api/auth/register", { email, password });
2002
+ return [
2003
+ "Account Registered",
2004
+ "",
2005
+ `User ID: ${data.id}`,
2006
+ `Email: ${data.email}`,
2007
+ `Email Verified: ${data.emailVerified ? "Yes" : "No"}`,
2008
+ `Created: ${data.createdAt}`,
2009
+ "",
2010
+ "Next steps:",
2011
+ " 1. Check your email for a verification code",
2012
+ " 2. Use verify_email to verify your email address",
2013
+ " 3. Set DOMINUSNODE_API_KEY to start using the proxy",
2014
+ ].join("\n");
2015
+ }
2016
+ catch (err) {
2017
+ return `Error: ${safeError(err)}`;
2018
+ }
2019
+ },
2020
+ };
2021
+ // 34. login
2022
+ const loginTool = {
2023
+ name: "login",
2024
+ description: "Log in to a Dominus Node account and receive access/refresh tokens. " +
2025
+ "This is an unauthenticated endpoint -- no API key required.",
2026
+ parameters: {
2027
+ email: {
2028
+ type: "string",
2029
+ description: "Account email address",
2030
+ required: true,
2031
+ },
2032
+ password: {
2033
+ type: "string",
2034
+ description: "Account password (8-128 characters)",
2035
+ required: true,
2036
+ },
2037
+ },
2038
+ execute: async (args) => {
2039
+ try {
2040
+ const email = String(args.email ?? "").trim();
2041
+ if (!email || !EMAIL_RE.test(email)) {
2042
+ return "Error: A valid email address is required.";
2043
+ }
2044
+ if (email.length > 254) {
2045
+ return "Error: Email address is too long (max 254 characters).";
2046
+ }
2047
+ const password = String(args.password ?? "");
2048
+ if (password.length < 8 || password.length > 128) {
2049
+ return "Error: Password must be 8-128 characters.";
2050
+ }
2051
+ const data = await unauthenticatedRequest("POST", "/api/auth/login", { email, password });
2052
+ if (data.mfaRequired) {
2053
+ return [
2054
+ "MFA Required",
2055
+ "",
2056
+ "This account has multi-factor authentication enabled.",
2057
+ "Please complete MFA verification to continue.",
2058
+ ].join("\n");
2059
+ }
2060
+ return [
2061
+ "Login Successful",
2062
+ "",
2063
+ `Access Token: ${scrubCredentials(data.accessToken ?? "")}`,
2064
+ `Refresh Token: ${scrubCredentials(data.refreshToken ?? "")}`,
2065
+ `Expires In: ${data.expiresIn ?? 900} seconds`,
2066
+ "",
2067
+ "Use these tokens for authenticated API requests.",
2068
+ ].join("\n");
2069
+ }
2070
+ catch (err) {
2071
+ return `Error: ${safeError(err)}`;
2072
+ }
2073
+ },
2074
+ };
2075
+ // 35. get_account_info
2076
+ const getAccountInfoTool = {
2077
+ name: "get_account_info",
2078
+ description: "Get your Dominus Node account information including email, plan, verification status, and creation date.",
2079
+ parameters: {},
2080
+ execute: async () => {
2081
+ try {
2082
+ const data = await apiGet("/api/auth/me");
2083
+ return [
2084
+ "Account Info",
2085
+ "",
2086
+ `User ID: ${data.id}`,
2087
+ `Email: ${data.email}`,
2088
+ `Email Verified: ${data.emailVerified ? "Yes" : "No"}`,
2089
+ `Plan: ${data.plan}`,
2090
+ `Status: ${data.status}`,
2091
+ `MFA Enabled: ${data.mfaEnabled ? "Yes" : "No"}`,
2092
+ `Created: ${data.createdAt}`,
2093
+ ].join("\n");
2094
+ }
2095
+ catch (err) {
2096
+ return `Error: ${safeError(err)}`;
2097
+ }
2098
+ },
2099
+ };
2100
+ // 36. verify_email
2101
+ const verifyEmailTool = {
2102
+ name: "verify_email",
2103
+ description: "Verify your email address using the code sent during registration. " +
2104
+ "This is an unauthenticated endpoint -- no API key required.",
2105
+ parameters: {
2106
+ email: {
2107
+ type: "string",
2108
+ description: "Email address to verify",
2109
+ required: true,
2110
+ },
2111
+ code: {
2112
+ type: "string",
2113
+ description: "Verification code from email",
2114
+ required: true,
2115
+ },
2116
+ },
2117
+ execute: async (args) => {
2118
+ try {
2119
+ const email = String(args.email ?? "").trim();
2120
+ if (!email || !EMAIL_RE.test(email)) {
2121
+ return "Error: A valid email address is required.";
2122
+ }
2123
+ if (email.length > 254) {
2124
+ return "Error: Email address is too long (max 254 characters).";
2125
+ }
2126
+ const code = String(args.code ?? "").trim();
2127
+ if (!code || code.length === 0 || code.length > 100) {
2128
+ return "Error: Verification code is required (max 100 characters).";
2129
+ }
2130
+ if (/[\x00-\x1f\x7f]/.test(code)) {
2131
+ return "Error: Verification code contains invalid control characters.";
2132
+ }
2133
+ await unauthenticatedRequest("POST", "/api/auth/verify-email", { email, code });
2134
+ return [
2135
+ "Email Verified Successfully",
2136
+ "",
2137
+ `Email: ${email}`,
2138
+ "",
2139
+ "Your account is now fully activated. You can start using the proxy.",
2140
+ ].join("\n");
2141
+ }
2142
+ catch (err) {
2143
+ return `Error: ${safeError(err)}`;
2144
+ }
2145
+ },
2146
+ };
2147
+ // 37. resend_verification
2148
+ const resendVerificationTool = {
2149
+ name: "resend_verification",
2150
+ description: "Resend the email verification code. Requires authentication.",
2151
+ parameters: {},
2152
+ execute: async () => {
2153
+ try {
2154
+ await apiPost("/api/auth/resend-verification");
2155
+ return [
2156
+ "Verification Email Resent",
2157
+ "",
2158
+ "A new verification code has been sent to your registered email address.",
2159
+ "Use verify_email with the new code to complete verification.",
2160
+ ].join("\n");
2161
+ }
2162
+ catch (err) {
2163
+ return `Error: ${safeError(err)}`;
2164
+ }
2165
+ },
2166
+ };
2167
+ // 38. update_password
2168
+ const updatePasswordTool = {
2169
+ name: "update_password",
2170
+ description: "Change your account password. Requires your current password for verification.",
2171
+ parameters: {
2172
+ current_password: {
2173
+ type: "string",
2174
+ description: "Your current password (8-128 characters)",
2175
+ required: true,
2176
+ },
2177
+ new_password: {
2178
+ type: "string",
2179
+ description: "Your new password (8-128 characters)",
2180
+ required: true,
2181
+ },
2182
+ },
2183
+ execute: async (args) => {
2184
+ try {
2185
+ const currentPassword = String(args.current_password ?? "");
2186
+ if (currentPassword.length < 8 || currentPassword.length > 128) {
2187
+ return "Error: Current password must be 8-128 characters.";
2188
+ }
2189
+ const newPassword = String(args.new_password ?? "");
2190
+ if (newPassword.length < 8 || newPassword.length > 128) {
2191
+ return "Error: New password must be 8-128 characters.";
2192
+ }
2193
+ await apiPost("/api/auth/change-password", {
2194
+ currentPassword,
2195
+ newPassword,
2196
+ });
2197
+ return [
2198
+ "Password Updated Successfully",
2199
+ "",
2200
+ "Your password has been changed.",
2201
+ "All existing sessions remain valid.",
2202
+ ].join("\n");
2203
+ }
2204
+ catch (err) {
2205
+ return `Error: ${safeError(err)}`;
2206
+ }
2207
+ },
2208
+ };
2209
+ // 39. list_keys
2210
+ const listKeysTool = {
2211
+ name: "list_keys",
2212
+ description: "List all your personal API keys. Shows key prefix, label, status, and creation date.",
2213
+ parameters: {},
2214
+ execute: async () => {
2215
+ try {
2216
+ const data = await apiGet("/api/keys");
2217
+ const keys = data.keys ?? [];
2218
+ if (keys.length === 0) {
2219
+ return "No API keys found. Use create_key to create one.";
2220
+ }
2221
+ const lines = [`API Keys (${keys.length})`, ""];
2222
+ for (const k of keys) {
2223
+ const lastUsed = k.lastUsedAt ? ` | Last used: ${k.lastUsedAt}` : "";
2224
+ lines.push(` ${k.prefix}... | ${k.label} | ${k.status}${lastUsed}`);
2225
+ lines.push(` ID: ${k.id} | Created: ${k.createdAt}`);
2226
+ lines.push("");
2227
+ }
2228
+ return truncate(lines.join("\n"));
2229
+ }
2230
+ catch (err) {
2231
+ return `Error: ${safeError(err)}`;
2232
+ }
2233
+ },
2234
+ };
2235
+ // 40. create_key
2236
+ const createKeyTool = {
2237
+ name: "create_key",
2238
+ description: "Create a new personal API key. The full key is shown only once -- save it immediately.",
2239
+ parameters: {
2240
+ label: {
2241
+ type: "string",
2242
+ description: 'Label for the API key (e.g., "production", "dev")',
2243
+ required: true,
2244
+ },
2245
+ },
2246
+ execute: async (args) => {
2247
+ try {
2248
+ const label = String(args.label ?? "").trim();
2249
+ if (!label || label.length === 0 || label.length > 100) {
2250
+ return "Error: label is required and must be 1-100 characters.";
2251
+ }
2252
+ if (/[\x00-\x1f\x7f]/.test(label)) {
2253
+ return "Error: label contains invalid control characters.";
2254
+ }
2255
+ const data = await apiPost("/api/keys", { label });
2256
+ return [
2257
+ "API Key Created",
2258
+ "",
2259
+ `Key ID: ${data.id}`,
2260
+ `API Key: ${data.key}`,
2261
+ `Prefix: ${data.prefix}`,
2262
+ `Label: ${data.label}`,
2263
+ `Created: ${data.createdAt}`,
2264
+ "",
2265
+ "IMPORTANT: Save this API key now -- it will not be shown again.",
2266
+ ].join("\n");
2267
+ }
2268
+ catch (err) {
2269
+ return `Error: ${safeError(err)}`;
2270
+ }
2271
+ },
2272
+ };
2273
+ // 41. revoke_key
2274
+ const revokeKeyTool = {
2275
+ name: "revoke_key",
2276
+ description: "Revoke (delete) a personal API key. This immediately invalidates the key.",
2277
+ parameters: {
2278
+ key_id: {
2279
+ type: "string",
2280
+ description: "API key ID (UUID) to revoke",
2281
+ required: true,
2282
+ },
2283
+ },
2284
+ execute: async (args) => {
2285
+ try {
2286
+ const keyId = validateUuid(String(args.key_id ?? ""), "key_id");
2287
+ await apiDelete(`/api/keys/${encodeURIComponent(keyId)}`);
2288
+ return [
2289
+ "API Key Revoked",
2290
+ "",
2291
+ `Key ID: ${keyId}`,
2292
+ "",
2293
+ "The key has been permanently revoked and can no longer be used.",
2294
+ ].join("\n");
2295
+ }
2296
+ catch (err) {
2297
+ return `Error: ${safeError(err)}`;
2298
+ }
2299
+ },
2300
+ };
2301
+ // 42. get_plan
2302
+ const getPlanTool = {
2303
+ name: "get_plan",
2304
+ description: "Get your current plan details including limits, pricing, and features.",
2305
+ parameters: {},
2306
+ execute: async () => {
2307
+ try {
2308
+ const data = await apiGet("/api/plans/user/plan");
2309
+ const lines = [
2310
+ "Current Plan",
2311
+ "",
2312
+ `Plan: ${data.displayName ?? data.plan}`,
2313
+ `Bandwidth Limit: ${data.bandwidthLimitBytes != null ? formatBytes(data.bandwidthLimitBytes) : "Unlimited"}`,
2314
+ `Rate Limit: ${data.requestsPerMinute} req/min`,
2315
+ `Max API Keys: ${data.maxApiKeys}`,
2316
+ `Max Agentic Wallets: ${data.maxAgenticWallets}`,
2317
+ `Max Teams: ${data.maxTeams}`,
2318
+ ];
2319
+ const features = data.features ?? [];
2320
+ if (features.length > 0) {
2321
+ lines.push("", "Features:");
2322
+ for (const f of features) {
2323
+ lines.push(` - ${f}`);
2324
+ }
2325
+ }
2326
+ lines.push("", "Use list_plans to see available plan options.");
2327
+ return lines.join("\n");
2328
+ }
2329
+ catch (err) {
2330
+ return `Error: ${safeError(err)}`;
2331
+ }
2332
+ },
2333
+ };
2334
+ // 43. list_plans
2335
+ const listPlansTool = {
2336
+ name: "list_plans",
2337
+ description: "List all available plans with pricing, limits, and features.",
2338
+ parameters: {},
2339
+ execute: async () => {
2340
+ try {
2341
+ const data = await apiGet("/api/plans");
2342
+ const plans = data.plans ?? [];
2343
+ if (plans.length === 0) {
2344
+ return "No plans available.";
2345
+ }
2346
+ const lines = [`Available Plans (${plans.length})`, ""];
2347
+ for (const p of plans) {
2348
+ lines.push(` ${p.displayName ?? p.name} ($${(p.priceMonthly / 100).toFixed(2)}/mo)`);
2349
+ lines.push(` Bandwidth: ${p.bandwidthLimitBytes != null ? formatBytes(p.bandwidthLimitBytes) : "Unlimited"} | Rate: ${p.requestsPerMinute} req/min | Keys: ${p.maxApiKeys}`);
2350
+ if (p.features && p.features.length > 0) {
2351
+ lines.push(` Features: ${p.features.join(", ")}`);
2352
+ }
2353
+ lines.push("");
2354
+ }
2355
+ lines.push("Use change_plan to switch plans.");
2356
+ return truncate(lines.join("\n"));
2357
+ }
2358
+ catch (err) {
2359
+ return `Error: ${safeError(err)}`;
2360
+ }
2361
+ },
2362
+ };
2363
+ // 44. change_plan
2364
+ const changePlanTool = {
2365
+ name: "change_plan",
2366
+ description: "Change your account plan. Use list_plans to see available options first.",
2367
+ parameters: {
2368
+ plan: {
2369
+ type: "string",
2370
+ description: "Plan name/ID to switch to",
2371
+ required: true,
2372
+ },
2373
+ },
2374
+ execute: async (args) => {
2375
+ try {
2376
+ const plan = String(args.plan ?? "").trim();
2377
+ if (!plan || plan.length === 0 || plan.length > 100) {
2378
+ return "Error: plan is required and must be 1-100 characters.";
2379
+ }
2380
+ if (/[\x00-\x1f\x7f]/.test(plan)) {
2381
+ return "Error: plan name contains invalid control characters.";
2382
+ }
2383
+ const data = await apiPut("/api/plans/user/plan", { plan });
2384
+ return [
2385
+ "Plan Changed Successfully",
2386
+ "",
2387
+ `New Plan: ${data.displayName ?? data.plan}`,
2388
+ `Effective: ${data.effectiveAt ?? "immediately"}`,
2389
+ "",
2390
+ "Use get_plan to view your updated plan details.",
2391
+ ].join("\n");
2392
+ }
2393
+ catch (err) {
2394
+ return `Error: ${safeError(err)}`;
2395
+ }
2396
+ },
2397
+ };
2398
+ // 45. team_delete
2399
+ const teamDeleteTool = {
2400
+ name: "team_delete",
2401
+ description: "Delete a team. Only the team owner can delete a team. Any remaining team balance is refunded.",
2402
+ parameters: {
2403
+ team_id: {
2404
+ type: "string",
2405
+ description: "Team ID (UUID) to delete",
2406
+ required: true,
2407
+ },
2408
+ },
2409
+ execute: async (args) => {
2410
+ try {
2411
+ const teamId = validateUuid(String(args.team_id ?? ""), "team_id");
2412
+ await apiDelete(`/api/teams/${encodeURIComponent(teamId)}`);
2413
+ return [
2414
+ "Team Deleted",
2415
+ "",
2416
+ `Team ID: ${teamId}`,
2417
+ "",
2418
+ "The team has been permanently deleted.",
2419
+ "Any remaining balance has been refunded to the owner's wallet.",
2420
+ ].join("\n");
2421
+ }
2422
+ catch (err) {
2423
+ return `Error: ${safeError(err)}`;
2424
+ }
2425
+ },
2426
+ };
2427
+ // 46. team_revoke_key
2428
+ const teamRevokeKeyTool = {
2429
+ name: "team_revoke_key",
2430
+ description: "Revoke a shared API key for a team. Only owners and admins can revoke keys.",
2431
+ parameters: {
2432
+ team_id: {
2433
+ type: "string",
2434
+ description: "Team ID (UUID)",
2435
+ required: true,
2436
+ },
2437
+ key_id: {
2438
+ type: "string",
2439
+ description: "API key ID (UUID) to revoke",
2440
+ required: true,
2441
+ },
2442
+ },
2443
+ execute: async (args) => {
2444
+ try {
2445
+ const teamId = validateUuid(String(args.team_id ?? ""), "team_id");
2446
+ const keyId = validateUuid(String(args.key_id ?? ""), "key_id");
2447
+ await apiDelete(`/api/teams/${encodeURIComponent(teamId)}/keys/${encodeURIComponent(keyId)}`);
2448
+ return [
2449
+ "Team API Key Revoked",
2450
+ "",
2451
+ `Team ID: ${teamId}`,
2452
+ `Key ID: ${keyId}`,
2453
+ "",
2454
+ "The key has been permanently revoked and can no longer be used.",
2455
+ ].join("\n");
2456
+ }
2457
+ catch (err) {
2458
+ return `Error: ${safeError(err)}`;
2459
+ }
2460
+ },
2461
+ };
2462
+ // 47. team_list_keys
2463
+ const teamListKeysTool = {
2464
+ name: "team_list_keys",
2465
+ description: "List all API keys for a team. Shows key prefix, label, status, and creation date.",
2466
+ parameters: {
2467
+ team_id: {
2468
+ type: "string",
2469
+ description: "Team ID (UUID)",
2470
+ required: true,
2471
+ },
2472
+ },
2473
+ execute: async (args) => {
2474
+ try {
2475
+ const teamId = validateUuid(String(args.team_id ?? ""), "team_id");
2476
+ const data = await apiGet(`/api/teams/${encodeURIComponent(teamId)}/keys`);
2477
+ const keys = data.keys ?? [];
2478
+ if (keys.length === 0) {
2479
+ return "No API keys found for this team. Use team_create_key to create one.";
2480
+ }
2481
+ const lines = [`Team API Keys (${keys.length})`, ""];
2482
+ for (const k of keys) {
2483
+ const lastUsed = k.lastUsedAt ? ` | Last used: ${k.lastUsedAt}` : "";
2484
+ lines.push(` ${k.prefix}... | ${k.label} | ${k.status}${lastUsed}`);
2485
+ lines.push(` ID: ${k.id} | Created: ${k.createdAt}`);
2486
+ lines.push("");
2487
+ }
2488
+ return truncate(lines.join("\n"));
2489
+ }
2490
+ catch (err) {
2491
+ return `Error: ${safeError(err)}`;
2492
+ }
2493
+ },
2494
+ };
2495
+ // 48. team_list_members
2496
+ const teamListMembersTool = {
2497
+ name: "team_list_members",
2498
+ description: "List all members of a team with their roles and join dates.",
2499
+ parameters: {
2500
+ team_id: {
2501
+ type: "string",
2502
+ description: "Team ID (UUID)",
2503
+ required: true,
2504
+ },
2505
+ },
2506
+ execute: async (args) => {
2507
+ try {
2508
+ const teamId = validateUuid(String(args.team_id ?? ""), "team_id");
2509
+ const data = await apiGet(`/api/teams/${encodeURIComponent(teamId)}/members`);
2510
+ const members = data.members ?? [];
2511
+ if (members.length === 0) {
2512
+ return "No members found in this team.";
2513
+ }
2514
+ const lines = [`Team Members (${members.length})`, ""];
2515
+ for (const m of members) {
2516
+ lines.push(` ${m.email} | ${m.role} | Joined: ${m.joinedAt}`);
2517
+ lines.push(` User ID: ${m.userId}`);
2518
+ lines.push("");
2519
+ }
2520
+ return truncate(lines.join("\n"));
2521
+ }
2522
+ catch (err) {
2523
+ return `Error: ${safeError(err)}`;
2524
+ }
2525
+ },
2526
+ };
2527
+ // 49. team_add_member
2528
+ const teamAddMemberTool = {
2529
+ name: "team_add_member",
2530
+ description: "Add a member directly to a team by user ID. Only owners and admins can add members.",
2531
+ parameters: {
2532
+ team_id: {
2533
+ type: "string",
2534
+ description: "Team ID (UUID)",
2535
+ required: true,
2536
+ },
2537
+ user_id: {
2538
+ type: "string",
2539
+ description: "User ID (UUID) of the person to add",
2540
+ required: true,
2541
+ },
2542
+ role: {
2543
+ type: "string",
2544
+ description: "Role for the new member",
2545
+ required: false,
2546
+ enum: ["member", "admin"],
2547
+ default: "member",
2548
+ },
2549
+ },
2550
+ execute: async (args) => {
2551
+ try {
2552
+ const teamId = validateUuid(String(args.team_id ?? ""), "team_id");
2553
+ const userId = validateUuid(String(args.user_id ?? ""), "user_id");
2554
+ const role = String(args.role ?? "member").trim();
2555
+ if (role !== "member" && role !== "admin") {
2556
+ return "Error: role must be 'member' or 'admin'.";
2557
+ }
2558
+ const data = await apiPost(`/api/teams/${encodeURIComponent(teamId)}/members`, {
2559
+ userId,
2560
+ role,
2561
+ });
2562
+ return [
2563
+ "Team Member Added",
2564
+ "",
2565
+ `Team ID: ${data.teamId ?? teamId}`,
2566
+ `User ID: ${data.userId ?? userId}`,
2567
+ `Role: ${data.role ?? role}`,
2568
+ `Joined: ${data.joinedAt ?? "now"}`,
2569
+ ].join("\n");
2570
+ }
2571
+ catch (err) {
2572
+ return `Error: ${safeError(err)}`;
2573
+ }
2574
+ },
2575
+ };
2576
+ // 50. team_remove_member
2577
+ const teamRemoveMemberTool = {
2578
+ name: "team_remove_member",
2579
+ description: "Remove a member from a team. Only owners and admins can remove members.",
2580
+ parameters: {
2581
+ team_id: {
2582
+ type: "string",
2583
+ description: "Team ID (UUID)",
2584
+ required: true,
2585
+ },
2586
+ user_id: {
2587
+ type: "string",
2588
+ description: "User ID (UUID) of the member to remove",
2589
+ required: true,
2590
+ },
2591
+ },
2592
+ execute: async (args) => {
2593
+ try {
2594
+ const teamId = validateUuid(String(args.team_id ?? ""), "team_id");
2595
+ const userId = validateUuid(String(args.user_id ?? ""), "user_id");
2596
+ await apiDelete(`/api/teams/${encodeURIComponent(teamId)}/members/${encodeURIComponent(userId)}`);
2597
+ return [
2598
+ "Team Member Removed",
2599
+ "",
2600
+ `Team ID: ${teamId}`,
2601
+ `User ID: ${userId}`,
2602
+ "",
2603
+ "The member has been removed from the team.",
2604
+ ].join("\n");
2605
+ }
2606
+ catch (err) {
2607
+ return `Error: ${safeError(err)}`;
2608
+ }
2609
+ },
2610
+ };
2611
+ // 51. team_invite_member
2612
+ const teamInviteMemberTool = {
2613
+ name: "team_invite_member",
2614
+ description: "Send an invitation to join a team by email. The invitee receives an email with a link to accept.",
2615
+ parameters: {
2616
+ team_id: {
2617
+ type: "string",
2618
+ description: "Team ID (UUID)",
2619
+ required: true,
2620
+ },
2621
+ email: {
2622
+ type: "string",
2623
+ description: "Email address of the person to invite",
2624
+ required: true,
2625
+ },
2626
+ role: {
2627
+ type: "string",
2628
+ description: "Role for the invited member",
2629
+ required: false,
2630
+ enum: ["member", "admin"],
2631
+ default: "member",
2632
+ },
2633
+ },
2634
+ execute: async (args) => {
2635
+ try {
2636
+ const teamId = validateUuid(String(args.team_id ?? ""), "team_id");
2637
+ const email = String(args.email ?? "").trim();
2638
+ if (!email || !EMAIL_RE.test(email)) {
2639
+ return "Error: A valid email address is required.";
2640
+ }
2641
+ if (email.length > 254) {
2642
+ return "Error: Email address is too long (max 254 characters).";
2643
+ }
2644
+ const role = String(args.role ?? "member").trim();
2645
+ if (role !== "member" && role !== "admin") {
2646
+ return "Error: role must be 'member' or 'admin'.";
2647
+ }
2648
+ const data = await apiPost(`/api/teams/${encodeURIComponent(teamId)}/invites`, {
2649
+ email,
2650
+ role,
2651
+ });
2652
+ return [
2653
+ "Team Invitation Sent",
2654
+ "",
2655
+ `Invite ID: ${data.id}`,
2656
+ `Team ID: ${data.teamId ?? teamId}`,
2657
+ `Email: ${data.email ?? email}`,
2658
+ `Role: ${data.role ?? role}`,
2659
+ `Status: ${data.status ?? "pending"}`,
2660
+ `Expires: ${data.expiresAt}`,
2661
+ "",
2662
+ "The invitee will receive an email with instructions to accept.",
2663
+ ].join("\n");
2664
+ }
2665
+ catch (err) {
2666
+ return `Error: ${safeError(err)}`;
2667
+ }
2668
+ },
2669
+ };
2670
+ // 52. team_list_invites
2671
+ const teamListInvitesTool = {
2672
+ name: "team_list_invites",
2673
+ description: "List all pending invitations for a team.",
2674
+ parameters: {
2675
+ team_id: {
2676
+ type: "string",
2677
+ description: "Team ID (UUID)",
2678
+ required: true,
2679
+ },
2680
+ },
2681
+ execute: async (args) => {
2682
+ try {
2683
+ const teamId = validateUuid(String(args.team_id ?? ""), "team_id");
2684
+ const data = await apiGet(`/api/teams/${encodeURIComponent(teamId)}/invites`);
2685
+ const invites = data.invites ?? [];
2686
+ if (invites.length === 0) {
2687
+ return "No pending invitations for this team.";
2688
+ }
2689
+ const lines = [`Team Invitations (${invites.length})`, ""];
2690
+ for (const inv of invites) {
2691
+ lines.push(` ${inv.email} | ${inv.role} | ${inv.status}`);
2692
+ lines.push(` ID: ${inv.id} | Expires: ${inv.expiresAt}`);
2693
+ lines.push("");
2694
+ }
2695
+ return truncate(lines.join("\n"));
2696
+ }
2697
+ catch (err) {
2698
+ return `Error: ${safeError(err)}`;
2699
+ }
2700
+ },
2701
+ };
2702
+ // 53. team_cancel_invite
2703
+ const teamCancelInviteTool = {
2704
+ name: "team_cancel_invite",
2705
+ description: "Cancel a pending team invitation. Only owners and admins can cancel invites.",
2706
+ parameters: {
2707
+ team_id: {
2708
+ type: "string",
2709
+ description: "Team ID (UUID)",
2710
+ required: true,
2711
+ },
2712
+ invite_id: {
2713
+ type: "string",
2714
+ description: "Invite ID (UUID) to cancel",
2715
+ required: true,
2716
+ },
2717
+ },
2718
+ execute: async (args) => {
2719
+ try {
2720
+ const teamId = validateUuid(String(args.team_id ?? ""), "team_id");
2721
+ const inviteId = validateUuid(String(args.invite_id ?? ""), "invite_id");
2722
+ await apiDelete(`/api/teams/${encodeURIComponent(teamId)}/invites/${encodeURIComponent(inviteId)}`);
2723
+ return [
2724
+ "Team Invitation Cancelled",
2725
+ "",
2726
+ `Team ID: ${teamId}`,
2727
+ `Invite ID: ${inviteId}`,
2728
+ "",
2729
+ "The invitation has been cancelled.",
2730
+ ].join("\n");
2731
+ }
2732
+ catch (err) {
2733
+ return `Error: ${safeError(err)}`;
2734
+ }
2735
+ },
2736
+ };
2737
+ // ---------------------------------------------------------------------------
2738
+ // Plugin export — the tools array that OpenClaw discovers
2739
+ // ---------------------------------------------------------------------------
2740
+ export const tools = [
2741
+ // Proxy (3)
2742
+ proxiedFetchTool,
2743
+ getProxyConfigTool,
2744
+ getProxyStatusTool,
2745
+ // Sessions (1)
2746
+ listSessionsTool,
2747
+ // Wallet (8)
2748
+ checkBalanceTool,
2749
+ getTransactionsTool,
2750
+ getForecastTool,
2751
+ topupPaypalTool,
2752
+ topupStripeTool,
2753
+ topupCryptoTool,
2754
+ checkPaymentTool,
2755
+ x402InfoTool,
2756
+ // Usage (3)
2757
+ checkUsageTool,
2758
+ getDailyUsageTool,
2759
+ getTopHostsTool,
2760
+ // Account (6)
2761
+ registerTool,
2762
+ loginTool,
2763
+ getAccountInfoTool,
2764
+ verifyEmailTool,
2765
+ resendVerificationTool,
2766
+ updatePasswordTool,
2767
+ // API Keys (3)
2768
+ listKeysTool,
2769
+ createKeyTool,
2770
+ revokeKeyTool,
2771
+ // Plans (3)
2772
+ getPlanTool,
2773
+ listPlansTool,
2774
+ changePlanTool,
2775
+ // Agentic Wallets (9)
2776
+ createAgenticWalletTool,
2777
+ fundAgenticWalletTool,
2778
+ checkAgenticBalanceTool,
2779
+ listAgenticWalletsTool,
2780
+ agenticTransactionsTool,
2781
+ freezeAgenticWalletTool,
2782
+ unfreezeAgenticWalletTool,
2783
+ deleteAgenticWalletTool,
2784
+ updateWalletPolicyTool,
2785
+ // Teams (17)
2786
+ createTeamTool,
2787
+ listTeamsTool,
2788
+ teamDetailsTool,
2789
+ updateTeamTool,
2790
+ teamDeleteTool,
2791
+ teamFundTool,
2792
+ teamCreateKeyTool,
2793
+ teamRevokeKeyTool,
2794
+ teamListKeysTool,
2795
+ teamUsageTool,
2796
+ teamListMembersTool,
2797
+ teamAddMemberTool,
2798
+ teamRemoveMemberTool,
2799
+ updateTeamMemberRoleTool,
2800
+ teamInviteMemberTool,
2801
+ teamListInvitesTool,
2802
+ teamCancelInviteTool,
1628
2803
  ];
1629
2804
  /**
1630
2805
  * Plugin metadata for OpenClaw discovery.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dominusnode/openclaw-plugin",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "Dominus Node proxy plugin for OpenClaw — route web requests through rotating proxy networks",
5
5
  "main": "dist/plugin.js",
6
6
  "types": "dist/plugin.d.ts",