@dominusnode/openclaw-plugin 1.2.0 → 1.5.2
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/README.md +89 -67
- package/dist/plugin.js +361 -47
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -37,10 +37,10 @@ cd Dominus Node/integrations/openclaw
|
|
|
37
37
|
|
|
38
38
|
## Configuration
|
|
39
39
|
|
|
40
|
-
| Environment Variable
|
|
41
|
-
|
|
42
|
-
| `DOMINUSNODE_API_KEY`
|
|
43
|
-
| `DOMINUSNODE_BASE_URL` | No
|
|
40
|
+
| Environment Variable | Required | Default | Description |
|
|
41
|
+
| ---------------------- | -------- | ----------------------------- | ---------------------------------------------------------------- |
|
|
42
|
+
| `DOMINUSNODE_API_KEY` | Yes | -- | Your Dominus Node API key (starts with `dn_live_` or `dn_test_`) |
|
|
43
|
+
| `DOMINUSNODE_BASE_URL` | No | `https://api.dominusnode.com` | Base URL for the Dominus Node REST API |
|
|
44
44
|
|
|
45
45
|
### Getting an API Key
|
|
46
46
|
|
|
@@ -54,147 +54,169 @@ cd Dominus Node/integrations/openclaw
|
|
|
54
54
|
### Proxy Tools
|
|
55
55
|
|
|
56
56
|
#### `proxied_fetch`
|
|
57
|
+
|
|
57
58
|
Fetch a URL through the proxy network with optional geo-targeting.
|
|
58
59
|
|
|
59
|
-
| Parameter | Type
|
|
60
|
-
|
|
61
|
-
| `url`
|
|
62
|
-
| `method`
|
|
63
|
-
| `country` | string | No
|
|
64
|
-
| `pool`
|
|
65
|
-
| `headers` | object | No
|
|
66
|
-
| `body`
|
|
60
|
+
| Parameter | Type | Required | Description |
|
|
61
|
+
| --------- | ------ | -------- | -------------------------------------------------------------------------- |
|
|
62
|
+
| `url` | string | Yes | The URL to fetch (http or https) |
|
|
63
|
+
| `method` | string | No | HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD). Default: GET |
|
|
64
|
+
| `country` | string | No | ISO 3166-1 alpha-2 country code (e.g., US, GB, DE) |
|
|
65
|
+
| `pool` | string | No | Proxy pool: `dc` (datacenter, $3/GB) or `residential` ($5/GB). Default: dc |
|
|
66
|
+
| `headers` | object | No | Additional HTTP headers as key-value pairs |
|
|
67
|
+
| `body` | string | No | Request body for POST/PUT/PATCH |
|
|
67
68
|
|
|
68
69
|
#### `get_proxy_config`
|
|
70
|
+
|
|
69
71
|
View available proxy pools, pricing, supported countries, and endpoint configuration. No parameters.
|
|
70
72
|
|
|
71
73
|
#### `list_sessions`
|
|
74
|
+
|
|
72
75
|
List all active proxy sessions with status, target hosts, and bandwidth usage. No parameters.
|
|
73
76
|
|
|
74
77
|
### Wallet Tools
|
|
75
78
|
|
|
76
79
|
#### `check_balance`
|
|
80
|
+
|
|
77
81
|
Check your wallet balance and estimated remaining bandwidth at current pricing. No parameters.
|
|
78
82
|
|
|
79
83
|
#### `check_usage`
|
|
84
|
+
|
|
80
85
|
View bandwidth usage statistics for a time period.
|
|
81
86
|
|
|
82
|
-
| Parameter | Type
|
|
83
|
-
|
|
84
|
-
| `days`
|
|
87
|
+
| Parameter | Type | Required | Description |
|
|
88
|
+
| --------- | ------ | -------- | ------------------------------------------------ |
|
|
89
|
+
| `days` | number | No | Number of days to look back (1-365). Default: 30 |
|
|
85
90
|
|
|
86
91
|
### Agentic Wallet Tools
|
|
87
92
|
|
|
88
93
|
Agentic wallets are server-side custodial sub-wallets designed for autonomous AI agents. They have per-transaction spending limits for safety and are funded from your main wallet.
|
|
89
94
|
|
|
90
95
|
#### `create_agentic_wallet`
|
|
96
|
+
|
|
91
97
|
Create a new agentic wallet.
|
|
92
98
|
|
|
93
|
-
| Parameter
|
|
94
|
-
|
|
95
|
-
| `label`
|
|
96
|
-
| `spending_limit_cents` | number | No
|
|
99
|
+
| Parameter | Type | Required | Description |
|
|
100
|
+
| ---------------------- | ------ | -------- | --------------------------------------------------------- |
|
|
101
|
+
| `label` | string | Yes | Label for the wallet (1-100 chars) |
|
|
102
|
+
| `spending_limit_cents` | number | No | Max spend per transaction in cents. Default: 10000 ($100) |
|
|
97
103
|
|
|
98
104
|
#### `fund_agentic_wallet`
|
|
105
|
+
|
|
99
106
|
Transfer funds from your main wallet to an agentic wallet.
|
|
100
107
|
|
|
101
|
-
| Parameter
|
|
102
|
-
|
|
103
|
-
| `wallet_id`
|
|
104
|
-
| `amount_cents` | number | Yes
|
|
108
|
+
| Parameter | Type | Required | Description |
|
|
109
|
+
| -------------- | ------ | -------- | ----------------------------- |
|
|
110
|
+
| `wallet_id` | string | Yes | Agentic wallet UUID |
|
|
111
|
+
| `amount_cents` | number | Yes | Amount in cents (100-1000000) |
|
|
105
112
|
|
|
106
113
|
#### `check_agentic_balance`
|
|
114
|
+
|
|
107
115
|
Check an agentic wallet's balance and status.
|
|
108
116
|
|
|
109
|
-
| Parameter
|
|
110
|
-
|
|
111
|
-
| `wallet_id` | string | Yes
|
|
117
|
+
| Parameter | Type | Required | Description |
|
|
118
|
+
| ----------- | ------ | -------- | ------------------- |
|
|
119
|
+
| `wallet_id` | string | Yes | Agentic wallet UUID |
|
|
112
120
|
|
|
113
121
|
#### `list_agentic_wallets`
|
|
122
|
+
|
|
114
123
|
List all your agentic wallets with balances and status. No parameters.
|
|
115
124
|
|
|
116
125
|
#### `agentic_transactions`
|
|
126
|
+
|
|
117
127
|
View transaction history for an agentic wallet.
|
|
118
128
|
|
|
119
|
-
| Parameter
|
|
120
|
-
|
|
121
|
-
| `wallet_id` | string | Yes
|
|
122
|
-
| `limit`
|
|
129
|
+
| Parameter | Type | Required | Description |
|
|
130
|
+
| ----------- | ------ | -------- | ------------------------------------------- |
|
|
131
|
+
| `wallet_id` | string | Yes | Agentic wallet UUID |
|
|
132
|
+
| `limit` | number | No | Number of transactions (1-100). Default: 20 |
|
|
123
133
|
|
|
124
134
|
### Team Tools
|
|
125
135
|
|
|
126
136
|
Teams enable shared wallet billing across multiple users. Owners and admins can manage members and create shared API keys.
|
|
127
137
|
|
|
128
138
|
#### `create_team`
|
|
139
|
+
|
|
129
140
|
Create a new team.
|
|
130
141
|
|
|
131
|
-
| Parameter
|
|
132
|
-
|
|
133
|
-
| `name`
|
|
134
|
-
| `max_members` | number | No
|
|
142
|
+
| Parameter | Type | Required | Description |
|
|
143
|
+
| ------------- | ------ | -------- | -------------------------------------------- |
|
|
144
|
+
| `name` | string | Yes | Team name (1-100 chars) |
|
|
145
|
+
| `max_members` | number | No | Maximum members (1-1000). Default: unlimited |
|
|
135
146
|
|
|
136
147
|
#### `list_teams`
|
|
148
|
+
|
|
137
149
|
List all teams you belong to. No parameters.
|
|
138
150
|
|
|
139
151
|
#### `team_details`
|
|
152
|
+
|
|
140
153
|
Get detailed info about a team.
|
|
141
154
|
|
|
142
|
-
| Parameter | Type
|
|
143
|
-
|
|
144
|
-
| `team_id` | string | Yes
|
|
155
|
+
| Parameter | Type | Required | Description |
|
|
156
|
+
| --------- | ------ | -------- | ----------- |
|
|
157
|
+
| `team_id` | string | Yes | Team UUID |
|
|
145
158
|
|
|
146
159
|
#### `team_fund`
|
|
160
|
+
|
|
147
161
|
Transfer funds from your personal wallet to a team wallet.
|
|
148
162
|
|
|
149
|
-
| Parameter
|
|
150
|
-
|
|
151
|
-
| `team_id`
|
|
152
|
-
| `amount_cents` | number | Yes
|
|
163
|
+
| Parameter | Type | Required | Description |
|
|
164
|
+
| -------------- | ------ | -------- | ----------------------------- |
|
|
165
|
+
| `team_id` | string | Yes | Team UUID |
|
|
166
|
+
| `amount_cents` | number | Yes | Amount in cents (100-1000000) |
|
|
153
167
|
|
|
154
168
|
#### `team_create_key`
|
|
169
|
+
|
|
155
170
|
Create a shared API key billed to the team wallet.
|
|
156
171
|
|
|
157
|
-
| Parameter | Type
|
|
158
|
-
|
|
159
|
-
| `team_id` | string | Yes
|
|
160
|
-
| `label`
|
|
172
|
+
| Parameter | Type | Required | Description |
|
|
173
|
+
| --------- | ------ | -------- | ----------------------- |
|
|
174
|
+
| `team_id` | string | Yes | Team UUID |
|
|
175
|
+
| `label` | string | Yes | Key label (1-100 chars) |
|
|
161
176
|
|
|
162
177
|
#### `team_usage`
|
|
178
|
+
|
|
163
179
|
View team wallet transaction history.
|
|
164
180
|
|
|
165
|
-
| Parameter | Type
|
|
166
|
-
|
|
167
|
-
| `team_id` | string | Yes
|
|
168
|
-
| `limit`
|
|
181
|
+
| Parameter | Type | Required | Description |
|
|
182
|
+
| --------- | ------ | -------- | ------------------------------------------- |
|
|
183
|
+
| `team_id` | string | Yes | Team UUID |
|
|
184
|
+
| `limit` | number | No | Number of transactions (1-100). Default: 20 |
|
|
169
185
|
|
|
170
186
|
## Usage Examples
|
|
171
187
|
|
|
172
188
|
### Fetch a page through a US proxy
|
|
189
|
+
|
|
173
190
|
```
|
|
174
191
|
Use proxied_fetch to get https://httpbin.org/ip through a US datacenter proxy
|
|
175
192
|
```
|
|
176
193
|
|
|
177
194
|
### Check your budget
|
|
195
|
+
|
|
178
196
|
```
|
|
179
197
|
Check my Dominus Node balance and tell me how much residential browsing I have left
|
|
180
198
|
```
|
|
181
199
|
|
|
182
200
|
### Set up a research team
|
|
201
|
+
|
|
183
202
|
```
|
|
184
203
|
Create a team called "Web Research" with max 5 members, then fund it with $25
|
|
185
204
|
```
|
|
186
205
|
|
|
187
206
|
### Create an agent wallet for automated scraping
|
|
207
|
+
|
|
188
208
|
```
|
|
189
209
|
Create an agentic wallet called "price-monitor" with a $5 spending limit, fund it with $10
|
|
190
210
|
```
|
|
191
211
|
|
|
192
212
|
### View usage breakdown
|
|
213
|
+
|
|
193
214
|
```
|
|
194
215
|
Show my Dominus Node usage for the last 7 days
|
|
195
216
|
```
|
|
196
217
|
|
|
197
218
|
### Fetch with residential proxy and geo-targeting
|
|
219
|
+
|
|
198
220
|
```
|
|
199
221
|
Use proxied_fetch to get https://example.co.uk through a GB residential proxy
|
|
200
222
|
```
|
|
@@ -218,24 +240,24 @@ The plugin implements comprehensive security measures:
|
|
|
218
240
|
|
|
219
241
|
## API Endpoints Used
|
|
220
242
|
|
|
221
|
-
| Tool
|
|
222
|
-
|
|
223
|
-
| `proxied_fetch`
|
|
224
|
-
| `check_balance`
|
|
225
|
-
| `check_usage`
|
|
226
|
-
| `get_proxy_config`
|
|
227
|
-
| `list_sessions`
|
|
228
|
-
| `create_agentic_wallet` | POST
|
|
229
|
-
| `fund_agentic_wallet`
|
|
230
|
-
| `check_agentic_balance` | GET
|
|
231
|
-
| `list_agentic_wallets`
|
|
232
|
-
| `agentic_transactions`
|
|
233
|
-
| `create_team`
|
|
234
|
-
| `list_teams`
|
|
235
|
-
| `team_details`
|
|
236
|
-
| `team_fund`
|
|
237
|
-
| `team_create_key`
|
|
238
|
-
| `team_usage`
|
|
243
|
+
| Tool | Method | Endpoint |
|
|
244
|
+
| ----------------------- | ------ | ------------------------------------ |
|
|
245
|
+
| `proxied_fetch` | POST | `/api/proxy/fetch` |
|
|
246
|
+
| `check_balance` | GET | `/api/wallet` |
|
|
247
|
+
| `check_usage` | GET | `/api/usage` |
|
|
248
|
+
| `get_proxy_config` | GET | `/api/proxy/config` |
|
|
249
|
+
| `list_sessions` | GET | `/api/sessions/active` |
|
|
250
|
+
| `create_agentic_wallet` | POST | `/api/agent-wallet` |
|
|
251
|
+
| `fund_agentic_wallet` | POST | `/api/agent-wallet/:id/fund` |
|
|
252
|
+
| `check_agentic_balance` | GET | `/api/agent-wallet/:id` |
|
|
253
|
+
| `list_agentic_wallets` | GET | `/api/agent-wallet` |
|
|
254
|
+
| `agentic_transactions` | GET | `/api/agent-wallet/:id/transactions` |
|
|
255
|
+
| `create_team` | POST | `/api/teams` |
|
|
256
|
+
| `list_teams` | GET | `/api/teams` |
|
|
257
|
+
| `team_details` | GET | `/api/teams/:id` |
|
|
258
|
+
| `team_fund` | POST | `/api/teams/:id/wallet/fund` |
|
|
259
|
+
| `team_create_key` | POST | `/api/teams/:id/keys` |
|
|
260
|
+
| `team_usage` | GET | `/api/teams/:id/wallet/transactions` |
|
|
239
261
|
|
|
240
262
|
## Requirements
|
|
241
263
|
|
package/dist/plugin.js
CHANGED
|
@@ -12,10 +12,63 @@
|
|
|
12
12
|
* - Response truncation at 4000 chars for LLM context efficiency
|
|
13
13
|
* - OFAC sanctioned country validation
|
|
14
14
|
*/
|
|
15
|
+
import * as crypto from "node:crypto";
|
|
15
16
|
import * as http from "node:http";
|
|
16
17
|
import * as tls from "node:tls";
|
|
17
18
|
import * as dns from "dns/promises";
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
20
|
+
// SHA-256 Proof-of-Work solver
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
function countLeadingZeroBits(buf) {
|
|
23
|
+
let count = 0;
|
|
24
|
+
for (const byte of buf) {
|
|
25
|
+
if (byte === 0) {
|
|
26
|
+
count += 8;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
let mask = 0x80;
|
|
30
|
+
while (mask && !(byte & mask)) {
|
|
31
|
+
count++;
|
|
32
|
+
mask >>= 1;
|
|
33
|
+
}
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
return count;
|
|
37
|
+
}
|
|
38
|
+
async function solvePoW(solveBaseUrl) {
|
|
39
|
+
try {
|
|
40
|
+
const resp = await fetch(`${solveBaseUrl}/api/auth/pow/challenge`, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: { "Content-Type": "application/json" },
|
|
43
|
+
redirect: "error",
|
|
44
|
+
});
|
|
45
|
+
if (!resp.ok)
|
|
46
|
+
return null;
|
|
47
|
+
const text = await resp.text();
|
|
48
|
+
if (text.length > 10_485_760)
|
|
49
|
+
return null;
|
|
50
|
+
const challenge = JSON.parse(text);
|
|
51
|
+
const prefix = challenge.prefix ?? "";
|
|
52
|
+
const difficulty = challenge.difficulty ?? 20;
|
|
53
|
+
const challengeId = challenge.challengeId ?? "";
|
|
54
|
+
if (!prefix || !challengeId)
|
|
55
|
+
return null;
|
|
56
|
+
for (let nonce = 0; nonce < 100_000_000; nonce++) {
|
|
57
|
+
const hash = crypto
|
|
58
|
+
.createHash("sha256")
|
|
59
|
+
.update(prefix + nonce.toString())
|
|
60
|
+
.digest();
|
|
61
|
+
if (countLeadingZeroBits(hash) >= difficulty) {
|
|
62
|
+
return { challengeId, nonce: nonce.toString() };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
19
72
|
// Configuration
|
|
20
73
|
// ---------------------------------------------------------------------------
|
|
21
74
|
const MAX_RESPONSE_CHARS = 4000;
|
|
@@ -69,7 +122,7 @@ function getAgentSecret() {
|
|
|
69
122
|
// ---------------------------------------------------------------------------
|
|
70
123
|
/** Remove any dn_live_* or dn_test_* tokens from error messages. */
|
|
71
124
|
function scrubCredentials(msg) {
|
|
72
|
-
return msg.replace(/dn_(live|test)_[A-Za-z0-9_-]+/g, "dn_$1_***REDACTED***");
|
|
125
|
+
return msg.replace(/dn_(live|test|proxy)_[A-Za-z0-9_-]+/g, "dn_$1_***REDACTED***");
|
|
73
126
|
}
|
|
74
127
|
function safeError(err) {
|
|
75
128
|
const raw = err instanceof Error ? err.message : String(err);
|
|
@@ -81,7 +134,8 @@ function safeError(err) {
|
|
|
81
134
|
function truncate(text, max = MAX_RESPONSE_CHARS) {
|
|
82
135
|
if (text.length <= max)
|
|
83
136
|
return text;
|
|
84
|
-
return text.slice(0, max) +
|
|
137
|
+
return (text.slice(0, max) +
|
|
138
|
+
`\n\n... [truncated, ${text.length - max} chars omitted]`);
|
|
85
139
|
}
|
|
86
140
|
// ---------------------------------------------------------------------------
|
|
87
141
|
// SSRF Protection
|
|
@@ -352,7 +406,9 @@ let jwtExpiresAt = 0;
|
|
|
352
406
|
let cachedApiKeyPrefix = null;
|
|
353
407
|
async function ensureAuth(apiKey, baseUrl) {
|
|
354
408
|
const keyPrefix = apiKey.slice(0, 16);
|
|
355
|
-
if (cachedJwt &&
|
|
409
|
+
if (cachedJwt &&
|
|
410
|
+
Date.now() < jwtExpiresAt &&
|
|
411
|
+
cachedApiKeyPrefix === keyPrefix)
|
|
356
412
|
return cachedJwt;
|
|
357
413
|
const authHeaders = {
|
|
358
414
|
"Content-Type": "application/json",
|
|
@@ -373,7 +429,7 @@ async function ensureAuth(apiKey, baseUrl) {
|
|
|
373
429
|
const text = await res.text().catch(() => "");
|
|
374
430
|
throw new Error(`Auth failed (${res.status}): ${scrubCredentials(text.slice(0, 500))}`);
|
|
375
431
|
}
|
|
376
|
-
const data = await res.json();
|
|
432
|
+
const data = (await res.json());
|
|
377
433
|
cachedApiKeyPrefix = keyPrefix;
|
|
378
434
|
cachedJwt = data.token;
|
|
379
435
|
// JWT expires in 15 min, refresh at 14 min for safety
|
|
@@ -386,8 +442,8 @@ async function apiRequest(method, path, body) {
|
|
|
386
442
|
const url = `${baseUrl}${path}`;
|
|
387
443
|
const jwt = await ensureAuth(apiKey, baseUrl);
|
|
388
444
|
const headers = {
|
|
389
|
-
|
|
390
|
-
|
|
445
|
+
Authorization: `Bearer ${jwt}`,
|
|
446
|
+
Accept: "application/json",
|
|
391
447
|
"User-Agent": "dominusnode-openclaw-plugin/1.0.0",
|
|
392
448
|
};
|
|
393
449
|
const agentSecret = getAgentSecret();
|
|
@@ -477,7 +533,7 @@ async function unauthenticatedRequest(method, path, body) {
|
|
|
477
533
|
const baseUrl = getBaseUrl();
|
|
478
534
|
const url = `${baseUrl}${path}`;
|
|
479
535
|
const headers = {
|
|
480
|
-
|
|
536
|
+
Accept: "application/json",
|
|
481
537
|
"User-Agent": "dominusnode-openclaw-plugin/1.0.0",
|
|
482
538
|
};
|
|
483
539
|
const agentSecret = getAgentSecret();
|
|
@@ -612,9 +668,19 @@ const proxiedFetchTool = {
|
|
|
612
668
|
}
|
|
613
669
|
const proxyType = String(args.pool ?? "dc");
|
|
614
670
|
// Validate and collect custom headers
|
|
615
|
-
const STRIPPED_HEADERS = new Set([
|
|
671
|
+
const STRIPPED_HEADERS = new Set([
|
|
672
|
+
"host",
|
|
673
|
+
"connection",
|
|
674
|
+
"content-length",
|
|
675
|
+
"transfer-encoding",
|
|
676
|
+
"proxy-authorization",
|
|
677
|
+
"authorization",
|
|
678
|
+
"user-agent",
|
|
679
|
+
]);
|
|
616
680
|
const customHeaders = {};
|
|
617
|
-
if (args.headers &&
|
|
681
|
+
if (args.headers &&
|
|
682
|
+
typeof args.headers === "object" &&
|
|
683
|
+
!Array.isArray(args.headers)) {
|
|
618
684
|
for (const [k, v] of Object.entries(args.headers)) {
|
|
619
685
|
const key = String(k);
|
|
620
686
|
const val = String(v ?? "");
|
|
@@ -639,15 +705,24 @@ const proxiedFetchTool = {
|
|
|
639
705
|
const parsed = new URL(url);
|
|
640
706
|
const MAX_RESP = 1_048_576; // 1MB
|
|
641
707
|
// Build custom header lines for raw HTTP request
|
|
642
|
-
const customHeaderLines = Object.entries(customHeaders)
|
|
708
|
+
const customHeaderLines = Object.entries(customHeaders)
|
|
709
|
+
.map(([k, v]) => `${k}: ${v}\r\n`)
|
|
710
|
+
.join("");
|
|
643
711
|
const result = await new Promise((resolve, reject) => {
|
|
644
712
|
const timer = setTimeout(() => reject(new Error("Proxy request timed out")), 30_000);
|
|
645
713
|
if (parsed.protocol === "https:") {
|
|
646
|
-
const connectHost = parsed.hostname.includes(":")
|
|
714
|
+
const connectHost = parsed.hostname.includes(":")
|
|
715
|
+
? `[${parsed.hostname}]`
|
|
716
|
+
: parsed.hostname;
|
|
647
717
|
const connectReq = http.request({
|
|
648
|
-
hostname: proxyHost,
|
|
718
|
+
hostname: proxyHost,
|
|
719
|
+
port: proxyPort,
|
|
720
|
+
method: "CONNECT",
|
|
649
721
|
path: `${connectHost}:${parsed.port || 443}`,
|
|
650
|
-
headers: {
|
|
722
|
+
headers: {
|
|
723
|
+
"Proxy-Authorization": proxyAuth,
|
|
724
|
+
Host: `${connectHost}:${parsed.port || 443}`,
|
|
725
|
+
},
|
|
651
726
|
});
|
|
652
727
|
connectReq.on("connect", (_res, sock) => {
|
|
653
728
|
if (_res.statusCode !== 200) {
|
|
@@ -656,13 +731,21 @@ const proxiedFetchTool = {
|
|
|
656
731
|
reject(new Error(`CONNECT failed: ${_res.statusCode}`));
|
|
657
732
|
return;
|
|
658
733
|
}
|
|
659
|
-
const tlsSock = tls.connect({
|
|
734
|
+
const tlsSock = tls.connect({
|
|
735
|
+
host: parsed.hostname,
|
|
736
|
+
socket: sock,
|
|
737
|
+
servername: parsed.hostname,
|
|
738
|
+
minVersion: "TLSv1.2",
|
|
739
|
+
}, () => {
|
|
660
740
|
const reqLine = `${method} ${parsed.pathname + parsed.search} HTTP/1.1\r\nHost: ${parsed.host}\r\nUser-Agent: dominusnode-openclaw/1.0.0\r\n${customHeaderLines}Connection: close\r\n\r\n`;
|
|
661
741
|
tlsSock.write(reqLine);
|
|
662
742
|
const chunks = [];
|
|
663
743
|
let bytes = 0;
|
|
664
|
-
tlsSock.on("data", (c) => {
|
|
665
|
-
|
|
744
|
+
tlsSock.on("data", (c) => {
|
|
745
|
+
bytes += c.length;
|
|
746
|
+
if (bytes <= MAX_RESP + 16384)
|
|
747
|
+
chunks.push(c);
|
|
748
|
+
});
|
|
666
749
|
let done = false;
|
|
667
750
|
const fin = () => {
|
|
668
751
|
if (done)
|
|
@@ -677,40 +760,69 @@ const proxiedFetchTool = {
|
|
|
677
760
|
}
|
|
678
761
|
const hdr = raw.substring(0, hEnd);
|
|
679
762
|
const body = raw.substring(hEnd + 4).substring(0, MAX_RESP);
|
|
680
|
-
const sm = hdr
|
|
763
|
+
const sm = hdr
|
|
764
|
+
.split("\r\n")[0]
|
|
765
|
+
.match(/^HTTP\/\d\.\d\s+(\d+)/);
|
|
681
766
|
const hdrs = {};
|
|
682
767
|
for (const l of hdr.split("\r\n").slice(1)) {
|
|
683
768
|
const ci = l.indexOf(":");
|
|
684
769
|
if (ci > 0)
|
|
685
|
-
hdrs[l.substring(0, ci).trim().toLowerCase()] = l
|
|
770
|
+
hdrs[l.substring(0, ci).trim().toLowerCase()] = l
|
|
771
|
+
.substring(ci + 1)
|
|
772
|
+
.trim();
|
|
686
773
|
}
|
|
687
|
-
resolve({
|
|
774
|
+
resolve({
|
|
775
|
+
status: sm ? parseInt(sm[1], 10) : 0,
|
|
776
|
+
headers: hdrs,
|
|
777
|
+
body,
|
|
778
|
+
});
|
|
688
779
|
};
|
|
689
780
|
tlsSock.on("end", fin);
|
|
690
781
|
tlsSock.on("close", fin);
|
|
691
|
-
tlsSock.on("error", (e) => {
|
|
782
|
+
tlsSock.on("error", (e) => {
|
|
783
|
+
clearTimeout(timer);
|
|
784
|
+
reject(e);
|
|
785
|
+
});
|
|
692
786
|
});
|
|
693
|
-
tlsSock.on("error", (e) => {
|
|
787
|
+
tlsSock.on("error", (e) => {
|
|
788
|
+
clearTimeout(timer);
|
|
789
|
+
reject(e);
|
|
790
|
+
});
|
|
791
|
+
});
|
|
792
|
+
connectReq.on("error", (e) => {
|
|
793
|
+
clearTimeout(timer);
|
|
794
|
+
reject(e);
|
|
694
795
|
});
|
|
695
|
-
connectReq.on("error", (e) => { clearTimeout(timer); reject(e); });
|
|
696
796
|
connectReq.end();
|
|
697
797
|
}
|
|
698
798
|
else {
|
|
699
799
|
const req = http.request({
|
|
700
|
-
hostname: proxyHost,
|
|
701
|
-
|
|
800
|
+
hostname: proxyHost,
|
|
801
|
+
port: proxyPort,
|
|
802
|
+
method,
|
|
803
|
+
path: url,
|
|
804
|
+
headers: {
|
|
805
|
+
"Proxy-Authorization": proxyAuth,
|
|
806
|
+
Host: parsed.host ?? "",
|
|
807
|
+
...customHeaders,
|
|
808
|
+
},
|
|
702
809
|
}, (res) => {
|
|
703
810
|
const chunks = [];
|
|
704
811
|
let bytes = 0;
|
|
705
|
-
res.on("data", (c) => {
|
|
706
|
-
|
|
812
|
+
res.on("data", (c) => {
|
|
813
|
+
bytes += c.length;
|
|
814
|
+
if (bytes <= MAX_RESP)
|
|
815
|
+
chunks.push(c);
|
|
816
|
+
});
|
|
707
817
|
let done = false;
|
|
708
818
|
const fin = () => {
|
|
709
819
|
if (done)
|
|
710
820
|
return;
|
|
711
821
|
done = true;
|
|
712
822
|
clearTimeout(timer);
|
|
713
|
-
const body = Buffer.concat(chunks)
|
|
823
|
+
const body = Buffer.concat(chunks)
|
|
824
|
+
.toString("utf-8")
|
|
825
|
+
.substring(0, MAX_RESP);
|
|
714
826
|
const hdrs = {};
|
|
715
827
|
for (const [k, v] of Object.entries(res.headers)) {
|
|
716
828
|
if (v)
|
|
@@ -720,9 +832,15 @@ const proxiedFetchTool = {
|
|
|
720
832
|
};
|
|
721
833
|
res.on("end", fin);
|
|
722
834
|
res.on("close", fin);
|
|
723
|
-
res.on("error", (e) => {
|
|
835
|
+
res.on("error", (e) => {
|
|
836
|
+
clearTimeout(timer);
|
|
837
|
+
reject(e);
|
|
838
|
+
});
|
|
839
|
+
});
|
|
840
|
+
req.on("error", (e) => {
|
|
841
|
+
clearTimeout(timer);
|
|
842
|
+
reject(e);
|
|
724
843
|
});
|
|
725
|
-
req.on("error", (e) => { clearTimeout(timer); reject(e); });
|
|
726
844
|
req.end();
|
|
727
845
|
}
|
|
728
846
|
});
|
|
@@ -733,7 +851,13 @@ const proxiedFetchTool = {
|
|
|
733
851
|
];
|
|
734
852
|
// Include relevant response headers
|
|
735
853
|
if (result.headers) {
|
|
736
|
-
const showHeaders = [
|
|
854
|
+
const showHeaders = [
|
|
855
|
+
"content-type",
|
|
856
|
+
"content-length",
|
|
857
|
+
"server",
|
|
858
|
+
"x-cache",
|
|
859
|
+
"cache-control",
|
|
860
|
+
];
|
|
737
861
|
for (const h of showHeaders) {
|
|
738
862
|
if (result.headers[h]) {
|
|
739
863
|
lines.push(`${h}: ${result.headers[h]}`);
|
|
@@ -920,9 +1044,12 @@ const createAgenticWalletTool = {
|
|
|
920
1044
|
spendingLimitCents,
|
|
921
1045
|
};
|
|
922
1046
|
// Validate optional daily_limit_cents
|
|
923
|
-
if (args.daily_limit_cents !== undefined &&
|
|
1047
|
+
if (args.daily_limit_cents !== undefined &&
|
|
1048
|
+
args.daily_limit_cents !== null) {
|
|
924
1049
|
const dailyLimit = Number(args.daily_limit_cents);
|
|
925
|
-
if (!Number.isInteger(dailyLimit) ||
|
|
1050
|
+
if (!Number.isInteger(dailyLimit) ||
|
|
1051
|
+
dailyLimit < 1 ||
|
|
1052
|
+
dailyLimit > 1000000) {
|
|
926
1053
|
return "Error: daily_limit_cents must be a positive integer between 1 and 1000000.";
|
|
927
1054
|
}
|
|
928
1055
|
body.dailyLimitCents = dailyLimit;
|
|
@@ -989,7 +1116,9 @@ const fundAgenticWalletTool = {
|
|
|
989
1116
|
try {
|
|
990
1117
|
const walletId = validateUuid(String(args.wallet_id ?? ""), "wallet_id");
|
|
991
1118
|
const amountCents = Number(args.amount_cents ?? 0);
|
|
992
|
-
if (!Number.isInteger(amountCents) ||
|
|
1119
|
+
if (!Number.isInteger(amountCents) ||
|
|
1120
|
+
amountCents < 100 ||
|
|
1121
|
+
amountCents > 1000000) {
|
|
993
1122
|
return "Error: amount_cents must be an integer between 100 ($1) and 1000000 ($10,000).";
|
|
994
1123
|
}
|
|
995
1124
|
const data = await apiPost(`/api/agent-wallet/${encodeURIComponent(walletId)}/fund`, {
|
|
@@ -1099,7 +1228,9 @@ const agenticTransactionsTool = {
|
|
|
1099
1228
|
const lines = [`Wallet Transactions (${txs.length})`, ""];
|
|
1100
1229
|
for (const tx of txs) {
|
|
1101
1230
|
const sign = tx.type === "fund" || tx.type === "refund" ? "+" : "-";
|
|
1102
|
-
const session = tx.sessionId
|
|
1231
|
+
const session = tx.sessionId
|
|
1232
|
+
? ` | Session: ${tx.sessionId.slice(0, 8)}`
|
|
1233
|
+
: "";
|
|
1103
1234
|
lines.push(` ${sign}${formatCents(tx.amountCents)} [${tx.type}] ${tx.description}`);
|
|
1104
1235
|
lines.push(` ${tx.createdAt}${session}`);
|
|
1105
1236
|
}
|
|
@@ -1138,7 +1269,9 @@ const createTeamTool = {
|
|
|
1138
1269
|
const body = { name };
|
|
1139
1270
|
if (args.max_members !== undefined) {
|
|
1140
1271
|
const maxMembers = Number(args.max_members);
|
|
1141
|
-
if (!Number.isInteger(maxMembers) ||
|
|
1272
|
+
if (!Number.isInteger(maxMembers) ||
|
|
1273
|
+
maxMembers < 1 ||
|
|
1274
|
+
maxMembers > 100) {
|
|
1142
1275
|
return "Error: max_members must be an integer between 1 and 100.";
|
|
1143
1276
|
}
|
|
1144
1277
|
body.maxMembers = maxMembers;
|
|
@@ -1250,7 +1383,9 @@ const teamFundTool = {
|
|
|
1250
1383
|
try {
|
|
1251
1384
|
const teamId = validateUuid(String(args.team_id ?? ""), "team_id");
|
|
1252
1385
|
const amountCents = Number(args.amount_cents ?? 0);
|
|
1253
|
-
if (!Number.isInteger(amountCents) ||
|
|
1386
|
+
if (!Number.isInteger(amountCents) ||
|
|
1387
|
+
amountCents < 100 ||
|
|
1388
|
+
amountCents > 1000000) {
|
|
1254
1389
|
return "Error: amount_cents must be an integer between 100 ($1) and 1000000 ($10,000).";
|
|
1255
1390
|
}
|
|
1256
1391
|
const data = await apiPost(`/api/teams/${encodeURIComponent(teamId)}/wallet/fund`, {
|
|
@@ -1490,7 +1625,9 @@ const updateTeamTool = {
|
|
|
1490
1625
|
}
|
|
1491
1626
|
if (args.max_members !== undefined) {
|
|
1492
1627
|
const maxMembers = Number(args.max_members);
|
|
1493
|
-
if (!Number.isInteger(maxMembers) ||
|
|
1628
|
+
if (!Number.isInteger(maxMembers) ||
|
|
1629
|
+
maxMembers < 1 ||
|
|
1630
|
+
maxMembers > 100) {
|
|
1494
1631
|
return "Error: max_members must be an integer between 1 and 100.";
|
|
1495
1632
|
}
|
|
1496
1633
|
body.maxMembers = maxMembers;
|
|
@@ -1528,7 +1665,9 @@ const topupPaypalTool = {
|
|
|
1528
1665
|
execute: async (args) => {
|
|
1529
1666
|
try {
|
|
1530
1667
|
const amountCents = Number(args.amount_cents ?? 0);
|
|
1531
|
-
if (!Number.isInteger(amountCents) ||
|
|
1668
|
+
if (!Number.isInteger(amountCents) ||
|
|
1669
|
+
amountCents < 500 ||
|
|
1670
|
+
amountCents > 100000) {
|
|
1532
1671
|
return "Error: amount_cents must be an integer between 500 ($5) and 100000 ($1,000).";
|
|
1533
1672
|
}
|
|
1534
1673
|
const data = await apiPost("/api/wallet/topup/paypal", { amountCents });
|
|
@@ -1563,7 +1702,9 @@ const topupStripeTool = {
|
|
|
1563
1702
|
execute: async (args) => {
|
|
1564
1703
|
try {
|
|
1565
1704
|
const amountCents = Number(args.amount_cents ?? 0);
|
|
1566
|
-
if (!Number.isInteger(amountCents) ||
|
|
1705
|
+
if (!Number.isInteger(amountCents) ||
|
|
1706
|
+
amountCents < 500 ||
|
|
1707
|
+
amountCents > 100000) {
|
|
1567
1708
|
return "Error: amount_cents must be an integer between 500 ($5) and 100000 ($1,000).";
|
|
1568
1709
|
}
|
|
1569
1710
|
const data = await apiPost("/api/wallet/topup/stripe", { amountCents });
|
|
@@ -1585,7 +1726,17 @@ const topupStripeTool = {
|
|
|
1585
1726
|
};
|
|
1586
1727
|
// 23. topup_crypto
|
|
1587
1728
|
const VALID_CRYPTO_CURRENCIES = new Set([
|
|
1588
|
-
"BTC",
|
|
1729
|
+
"BTC",
|
|
1730
|
+
"ETH",
|
|
1731
|
+
"LTC",
|
|
1732
|
+
"XMR",
|
|
1733
|
+
"ZEC",
|
|
1734
|
+
"USDC",
|
|
1735
|
+
"SOL",
|
|
1736
|
+
"USDT",
|
|
1737
|
+
"DAI",
|
|
1738
|
+
"BNB",
|
|
1739
|
+
"LINK",
|
|
1589
1740
|
]);
|
|
1590
1741
|
const topupCryptoTool = {
|
|
1591
1742
|
name: "topup_crypto",
|
|
@@ -1602,20 +1753,38 @@ const topupCryptoTool = {
|
|
|
1602
1753
|
type: "string",
|
|
1603
1754
|
description: "Cryptocurrency to pay with",
|
|
1604
1755
|
required: true,
|
|
1605
|
-
enum: [
|
|
1756
|
+
enum: [
|
|
1757
|
+
"BTC",
|
|
1758
|
+
"ETH",
|
|
1759
|
+
"LTC",
|
|
1760
|
+
"XMR",
|
|
1761
|
+
"ZEC",
|
|
1762
|
+
"USDC",
|
|
1763
|
+
"SOL",
|
|
1764
|
+
"USDT",
|
|
1765
|
+
"DAI",
|
|
1766
|
+
"BNB",
|
|
1767
|
+
"LINK",
|
|
1768
|
+
],
|
|
1606
1769
|
},
|
|
1607
1770
|
},
|
|
1608
1771
|
execute: async (args) => {
|
|
1609
1772
|
try {
|
|
1610
1773
|
const amountUsd = Number(args.amount_usd ?? 0);
|
|
1611
|
-
if (typeof amountUsd !== "number" ||
|
|
1774
|
+
if (typeof amountUsd !== "number" ||
|
|
1775
|
+
!Number.isFinite(amountUsd) ||
|
|
1776
|
+
amountUsd < 5 ||
|
|
1777
|
+
amountUsd > 1000) {
|
|
1612
1778
|
return "Error: amount_usd must be a number between 5 and 1000.";
|
|
1613
1779
|
}
|
|
1614
1780
|
const currency = String(args.currency ?? "").toUpperCase();
|
|
1615
1781
|
if (!VALID_CRYPTO_CURRENCIES.has(currency)) {
|
|
1616
1782
|
return `Error: currency must be one of: ${[...VALID_CRYPTO_CURRENCIES].join(", ")}.`;
|
|
1617
1783
|
}
|
|
1618
|
-
const data = await apiPost("/api/wallet/topup/crypto", {
|
|
1784
|
+
const data = await apiPost("/api/wallet/topup/crypto", {
|
|
1785
|
+
amountUsd,
|
|
1786
|
+
currency: currency.toLowerCase(),
|
|
1787
|
+
});
|
|
1619
1788
|
return [
|
|
1620
1789
|
"Crypto Payment Invoice Created",
|
|
1621
1790
|
"",
|
|
@@ -1727,7 +1896,9 @@ const updateWalletPolicyTool = {
|
|
|
1727
1896
|
}
|
|
1728
1897
|
else {
|
|
1729
1898
|
const dailyLimit = Number(args.daily_limit_cents);
|
|
1730
|
-
if (!Number.isInteger(dailyLimit) ||
|
|
1899
|
+
if (!Number.isInteger(dailyLimit) ||
|
|
1900
|
+
dailyLimit < 1 ||
|
|
1901
|
+
dailyLimit > 1000000) {
|
|
1731
1902
|
return "Error: daily_limit_cents must be a positive integer between 1 and 1000000, or null to clear.";
|
|
1732
1903
|
}
|
|
1733
1904
|
body.dailyLimitCents = dailyLimit;
|
|
@@ -1827,7 +1998,9 @@ const getTransactionsTool = {
|
|
|
1827
1998
|
}
|
|
1828
1999
|
const lines = [`Wallet Transactions (${txs.length})`, ""];
|
|
1829
2000
|
for (const tx of txs) {
|
|
1830
|
-
const sign = tx.type === "topup" || tx.type === "refund" || tx.type === "fund"
|
|
2001
|
+
const sign = tx.type === "topup" || tx.type === "refund" || tx.type === "fund"
|
|
2002
|
+
? "+"
|
|
2003
|
+
: "-";
|
|
1831
2004
|
lines.push(` ${sign}${formatCents(Math.abs(tx.amountCents))} [${tx.type}] ${tx.description}`);
|
|
1832
2005
|
lines.push(` ${tx.createdAt}`);
|
|
1833
2006
|
}
|
|
@@ -1998,7 +2171,12 @@ const registerTool = {
|
|
|
1998
2171
|
if (password.length < 8 || password.length > 128) {
|
|
1999
2172
|
return "Error: Password must be 8-128 characters.";
|
|
2000
2173
|
}
|
|
2001
|
-
|
|
2174
|
+
// Solve PoW for CAPTCHA-free registration
|
|
2175
|
+
const pow = await solvePoW(getBaseUrl());
|
|
2176
|
+
const regBody = { email, password };
|
|
2177
|
+
if (pow)
|
|
2178
|
+
regBody.pow = pow;
|
|
2179
|
+
const data = await unauthenticatedRequest("POST", "/api/auth/register", regBody);
|
|
2002
2180
|
return [
|
|
2003
2181
|
"Account Registered",
|
|
2004
2182
|
"",
|
|
@@ -2130,7 +2308,10 @@ const verifyEmailTool = {
|
|
|
2130
2308
|
if (/[\x00-\x1f\x7f]/.test(code)) {
|
|
2131
2309
|
return "Error: Verification code contains invalid control characters.";
|
|
2132
2310
|
}
|
|
2133
|
-
await unauthenticatedRequest("POST", "/api/auth/verify-email", {
|
|
2311
|
+
await unauthenticatedRequest("POST", "/api/auth/verify-email", {
|
|
2312
|
+
email,
|
|
2313
|
+
code,
|
|
2314
|
+
});
|
|
2134
2315
|
return [
|
|
2135
2316
|
"Email Verified Successfully",
|
|
2136
2317
|
"",
|
|
@@ -2735,6 +2916,134 @@ const teamCancelInviteTool = {
|
|
|
2735
2916
|
},
|
|
2736
2917
|
};
|
|
2737
2918
|
// ---------------------------------------------------------------------------
|
|
2919
|
+
// MPP (Machine Payment Protocol) tools
|
|
2920
|
+
// ---------------------------------------------------------------------------
|
|
2921
|
+
const mppInfoTool = {
|
|
2922
|
+
name: "mpp_info",
|
|
2923
|
+
description: "Get Machine Payment Protocol (MPP) information including enabled status, " +
|
|
2924
|
+
"supported payment methods, pricing, and session limits.",
|
|
2925
|
+
parameters: {},
|
|
2926
|
+
execute: async () => {
|
|
2927
|
+
try {
|
|
2928
|
+
const data = await apiGet("/api/mpp/info");
|
|
2929
|
+
return JSON.stringify(data, null, 2);
|
|
2930
|
+
}
|
|
2931
|
+
catch (err) {
|
|
2932
|
+
return `Error: ${safeError(err)}`;
|
|
2933
|
+
}
|
|
2934
|
+
},
|
|
2935
|
+
};
|
|
2936
|
+
const payMppTool = {
|
|
2937
|
+
name: "pay_mpp",
|
|
2938
|
+
description: "Top up wallet via Machine Payment Protocol (MPP). " +
|
|
2939
|
+
"Supports tempo, stripe_spt, and lightning payment methods.",
|
|
2940
|
+
parameters: {
|
|
2941
|
+
amount_cents: {
|
|
2942
|
+
type: "number",
|
|
2943
|
+
description: "Amount in cents to top up (min 500 = $5, max 100000 = $1,000)",
|
|
2944
|
+
required: true,
|
|
2945
|
+
},
|
|
2946
|
+
method: {
|
|
2947
|
+
type: "string",
|
|
2948
|
+
description: "MPP payment method",
|
|
2949
|
+
required: true,
|
|
2950
|
+
enum: ["tempo", "stripe_spt", "lightning"],
|
|
2951
|
+
},
|
|
2952
|
+
},
|
|
2953
|
+
execute: async (args) => {
|
|
2954
|
+
const amountCents = Number(args.amount_cents);
|
|
2955
|
+
const method = String(args.method ?? "");
|
|
2956
|
+
if (!Number.isInteger(amountCents) ||
|
|
2957
|
+
amountCents < 500 ||
|
|
2958
|
+
amountCents > 100_000) {
|
|
2959
|
+
return "Error: amount_cents must be an integer between 500 and 100,000";
|
|
2960
|
+
}
|
|
2961
|
+
if (!["tempo", "stripe_spt", "lightning"].includes(method)) {
|
|
2962
|
+
return "Error: method must be one of: tempo, stripe_spt, lightning";
|
|
2963
|
+
}
|
|
2964
|
+
try {
|
|
2965
|
+
const data = await apiPost("/api/mpp/topup", {
|
|
2966
|
+
amountCents,
|
|
2967
|
+
method,
|
|
2968
|
+
});
|
|
2969
|
+
return JSON.stringify(data, null, 2);
|
|
2970
|
+
}
|
|
2971
|
+
catch (err) {
|
|
2972
|
+
return `Error: ${safeError(err)}`;
|
|
2973
|
+
}
|
|
2974
|
+
},
|
|
2975
|
+
};
|
|
2976
|
+
const mppSessionOpenTool = {
|
|
2977
|
+
name: "mpp_session_open",
|
|
2978
|
+
description: "Open a pay-as-you-go MPP session. Returns a channelId for metered proxy usage.",
|
|
2979
|
+
parameters: {
|
|
2980
|
+
max_deposit_cents: {
|
|
2981
|
+
type: "number",
|
|
2982
|
+
description: "Maximum deposit in cents (min 500, max 100000)",
|
|
2983
|
+
required: true,
|
|
2984
|
+
},
|
|
2985
|
+
method: {
|
|
2986
|
+
type: "string",
|
|
2987
|
+
description: "MPP payment method",
|
|
2988
|
+
required: true,
|
|
2989
|
+
enum: ["tempo", "stripe_spt", "lightning"],
|
|
2990
|
+
},
|
|
2991
|
+
pool_type: {
|
|
2992
|
+
type: "string",
|
|
2993
|
+
description: "Proxy pool type: dc ($3/GB) or residential ($5/GB)",
|
|
2994
|
+
default: "dc",
|
|
2995
|
+
enum: ["dc", "residential"],
|
|
2996
|
+
},
|
|
2997
|
+
},
|
|
2998
|
+
execute: async (args) => {
|
|
2999
|
+
const maxDepositCents = Number(args.max_deposit_cents);
|
|
3000
|
+
const method = String(args.method ?? "");
|
|
3001
|
+
const poolType = String(args.pool_type ?? "dc");
|
|
3002
|
+
if (!Number.isInteger(maxDepositCents) ||
|
|
3003
|
+
maxDepositCents < 500 ||
|
|
3004
|
+
maxDepositCents > 100_000) {
|
|
3005
|
+
return "Error: max_deposit_cents must be an integer between 500 and 100,000";
|
|
3006
|
+
}
|
|
3007
|
+
if (!["tempo", "stripe_spt", "lightning"].includes(method)) {
|
|
3008
|
+
return "Error: method must be one of: tempo, stripe_spt, lightning";
|
|
3009
|
+
}
|
|
3010
|
+
if (!["dc", "residential"].includes(poolType)) {
|
|
3011
|
+
return "Error: pool_type must be dc or residential";
|
|
3012
|
+
}
|
|
3013
|
+
try {
|
|
3014
|
+
const data = await apiPost("/api/mpp/session/open", { maxDepositCents, method, poolType });
|
|
3015
|
+
return JSON.stringify(data, null, 2);
|
|
3016
|
+
}
|
|
3017
|
+
catch (err) {
|
|
3018
|
+
return `Error: ${safeError(err)}`;
|
|
3019
|
+
}
|
|
3020
|
+
},
|
|
3021
|
+
};
|
|
3022
|
+
const mppSessionCloseTool = {
|
|
3023
|
+
name: "mpp_session_close",
|
|
3024
|
+
description: "Close an MPP pay-as-you-go session. Returns the amount spent and refunded.",
|
|
3025
|
+
parameters: {
|
|
3026
|
+
channel_id: {
|
|
3027
|
+
type: "string",
|
|
3028
|
+
description: "The channelId returned from mpp_session_open",
|
|
3029
|
+
required: true,
|
|
3030
|
+
},
|
|
3031
|
+
},
|
|
3032
|
+
execute: async (args) => {
|
|
3033
|
+
const channelId = String(args.channel_id ?? "");
|
|
3034
|
+
if (!channelId) {
|
|
3035
|
+
return "Error: channel_id is required";
|
|
3036
|
+
}
|
|
3037
|
+
try {
|
|
3038
|
+
const data = await apiPost("/api/mpp/session/close", { channelId });
|
|
3039
|
+
return JSON.stringify(data, null, 2);
|
|
3040
|
+
}
|
|
3041
|
+
catch (err) {
|
|
3042
|
+
return `Error: ${safeError(err)}`;
|
|
3043
|
+
}
|
|
3044
|
+
},
|
|
3045
|
+
};
|
|
3046
|
+
// ---------------------------------------------------------------------------
|
|
2738
3047
|
// Plugin export — the tools array that OpenClaw discovers
|
|
2739
3048
|
// ---------------------------------------------------------------------------
|
|
2740
3049
|
export const tools = [
|
|
@@ -2800,6 +3109,11 @@ export const tools = [
|
|
|
2800
3109
|
teamInviteMemberTool,
|
|
2801
3110
|
teamListInvitesTool,
|
|
2802
3111
|
teamCancelInviteTool,
|
|
3112
|
+
// MPP (4)
|
|
3113
|
+
mppInfoTool,
|
|
3114
|
+
payMppTool,
|
|
3115
|
+
mppSessionOpenTool,
|
|
3116
|
+
mppSessionCloseTool,
|
|
2803
3117
|
];
|
|
2804
3118
|
/**
|
|
2805
3119
|
* Plugin metadata for OpenClaw discovery.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dominusnode/openclaw-plugin",
|
|
3
|
-
"version": "1.2
|
|
3
|
+
"version": "1.5.2",
|
|
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",
|
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsc",
|
|
11
|
+
"prepare": "npm run build",
|
|
11
12
|
"test": "vitest run"
|
|
12
13
|
},
|
|
13
14
|
"devDependencies": {
|
|
15
|
+
"@types/node": "^22.0.0",
|
|
14
16
|
"typescript": "^5.4.0",
|
|
15
17
|
"vitest": "^1.6.0"
|
|
16
18
|
},
|