nervepay 1.4.0 → 1.4.3
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 +45 -479
- package/bin/nervepay-cli.js +267 -140
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,86 +1,60 @@
|
|
|
1
|
-
# NervePay
|
|
1
|
+
# NervePay
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Self-sovereign identity for AI agents. W3C DIDs, Ed25519 cryptographic authentication, encrypted secrets vault, and multi-agent orchestration.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
- **🆔 Identity Management**: W3C DID registration with Ed25519 signatures
|
|
8
|
-
- **🔐 Vault**: AES-256-GCM encrypted secrets management
|
|
9
|
-
- **🔗 Gateway Pairing**: Connect OpenClaw gateways to NervePay Mission Control
|
|
10
|
-
- **🤖 Orchestration**: Multi-agent task decomposition and management
|
|
11
|
-
- **✅ Reputation**: Cross-platform agent reputation tracking
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
14
|
-
|
|
15
|
-
### Quick Start (Recommended)
|
|
5
|
+
## Quick Install
|
|
16
6
|
|
|
17
7
|
```bash
|
|
18
|
-
|
|
19
|
-
openclaw plugins install nervepay
|
|
20
|
-
|
|
21
|
-
# 2. Run interactive setup wizard
|
|
22
|
-
openclaw nervepay setup
|
|
23
|
-
|
|
24
|
-
# That's it! The wizard handles:
|
|
25
|
-
# ✅ Identity registration (W3C DID + Ed25519 keys)
|
|
26
|
-
# ✅ Human claim flow (optional)
|
|
27
|
-
# ✅ Gateway pairing (optional)
|
|
28
|
-
# ✅ Auto-configuration of OpenClaw
|
|
29
|
-
# ✅ Credentials backup
|
|
8
|
+
npx nervepay setup
|
|
30
9
|
```
|
|
31
10
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
### Manual Installation (from source)
|
|
11
|
+
**Alternatives:**
|
|
35
12
|
|
|
36
13
|
```bash
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
npm run build
|
|
41
|
-
openclaw plugins install .
|
|
42
|
-
openclaw nervepay setup # Still use the wizard!
|
|
14
|
+
bunx nervepay setup # Bun
|
|
15
|
+
pnpm dlx nervepay setup # pnpm
|
|
16
|
+
openclaw plugins install nervepay && openclaw nervepay setup # OpenClaw plugin
|
|
43
17
|
```
|
|
44
18
|
|
|
45
|
-
|
|
19
|
+
## What Happens
|
|
46
20
|
|
|
47
|
-
|
|
48
|
-
openclaw plugins install -l ./nervepay-plugin
|
|
49
|
-
openclaw nervepay setup
|
|
50
|
-
```
|
|
21
|
+
**1. Setup** — Creates your agent identity (W3C DID + Ed25519 keys), generates a BIP39 recovery phrase, and opens a claim URL to link the agent to your dashboard account.
|
|
51
22
|
|
|
52
|
-
|
|
23
|
+
**2. Pair** — Connects your OpenClaw gateway via the device node protocol (WebSocket challenge-response). Run `nervepay pair` if you skipped pairing during setup, then approve with `openclaw devices approve`.
|
|
53
24
|
|
|
54
|
-
|
|
25
|
+
## CLI Commands
|
|
55
26
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
27
|
+
| Command | Description |
|
|
28
|
+
|---------|-------------|
|
|
29
|
+
| `nervepay setup` | Create identity, claim agent, pair gateway |
|
|
30
|
+
| `nervepay pair` | Connect gateway via device node protocol |
|
|
31
|
+
| `nervepay whoami` | Show current agent identity and reputation |
|
|
32
|
+
| `nervepay gateways` | List connected gateways |
|
|
33
|
+
| `nervepay secrets` | List vault secrets (metadata only) |
|
|
34
|
+
| `nervepay status` | Show config and connection status |
|
|
35
|
+
|
|
36
|
+
All commands work with `npx`, `bunx`, or `pnpm dlx` prefixes.
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
59
39
|
|
|
60
|
-
|
|
61
|
-
1. Register your agent identity
|
|
62
|
-
2. Show claim URL (optional, improves trust)
|
|
63
|
-
3. Optionally pair your gateway
|
|
64
|
-
4. Auto-update your OpenClaw config
|
|
65
|
-
5. Save credentials securely to `~/.nervepay/credentials.json`
|
|
40
|
+
Setup auto-configures everything. Credentials are stored in:
|
|
66
41
|
|
|
67
|
-
|
|
42
|
+
- **OpenClaw config:** `~/.openclaw/openclaw.json` (plugin section)
|
|
43
|
+
- **Backup:** `~/.nervepay/credentials.json`
|
|
68
44
|
|
|
69
|
-
|
|
45
|
+
<details>
|
|
46
|
+
<summary>Manual configuration (advanced)</summary>
|
|
70
47
|
|
|
71
|
-
```
|
|
48
|
+
```json
|
|
72
49
|
{
|
|
73
50
|
"plugins": {
|
|
74
|
-
"enabled": true,
|
|
75
51
|
"entries": {
|
|
76
52
|
"nervepay": {
|
|
77
53
|
"enabled": true,
|
|
78
54
|
"config": {
|
|
79
55
|
"apiUrl": "https://api.nervepay.xyz",
|
|
80
56
|
"agentDid": "did:nervepay:agent:...",
|
|
81
|
-
"privateKey": "ed25519:..."
|
|
82
|
-
"enableOrchestration": false,
|
|
83
|
-
"enableAnalytics": true
|
|
57
|
+
"privateKey": "ed25519:..."
|
|
84
58
|
}
|
|
85
59
|
}
|
|
86
60
|
}
|
|
@@ -88,445 +62,37 @@ If you prefer to configure manually or already have credentials:
|
|
|
88
62
|
}
|
|
89
63
|
```
|
|
90
64
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
### Configuration Options
|
|
94
|
-
|
|
95
|
-
| Field | Type | Required | Description |
|
|
96
|
-
|-------|------|----------|-------------|
|
|
97
|
-
| `apiUrl` | string | No | NervePay API URL (default: https://api.nervepay.xyz) |
|
|
98
|
-
| `agentDid` | string | No* | Agent DID (auto-configured by setup wizard) |
|
|
99
|
-
| `privateKey` | string | No* | Ed25519 private key (auto-configured by setup wizard) |
|
|
100
|
-
| `enableOrchestration` | boolean | No | Enable multi-agent orchestration (default: false) |
|
|
101
|
-
| `enableAnalytics` | boolean | No | Enable analytics tracking (default: true) |
|
|
102
|
-
|
|
103
|
-
*Required for authenticated operations (vault, gateway, orchestration)
|
|
104
|
-
|
|
105
|
-
## Quick Start
|
|
106
|
-
|
|
107
|
-
### 🚀 The Easy Way (Interactive Setup)
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
# 1. Install
|
|
111
|
-
openclaw plugins install nervepay
|
|
112
|
-
|
|
113
|
-
# 2. Run setup wizard
|
|
114
|
-
openclaw nervepay setup
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
The wizard will ask you a few questions and configure everything automatically:
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
🔐 NervePay Setup Wizard
|
|
121
|
-
|
|
122
|
-
📋 Step 1: Agent Information
|
|
123
|
-
Agent name: My Research Bot
|
|
124
|
-
Agent description: Helps with literature reviews
|
|
125
|
-
|
|
126
|
-
🆔 Step 2: Registering Identity...
|
|
127
|
-
✅ Identity created!
|
|
128
|
-
DID: did:nervepay:agent:abc123...
|
|
129
|
-
|
|
130
|
-
👤 Step 3: Link to Human Owner
|
|
131
|
-
Link this agent to your NervePay account? [Y/n] y
|
|
132
|
-
Open this URL in your browser:
|
|
133
|
-
https://nervepay.xyz/claim/session-xyz
|
|
134
|
-
|
|
135
|
-
✅ Agent claimed successfully!
|
|
136
|
-
|
|
137
|
-
🔗 Step 4: Gateway Pairing
|
|
138
|
-
Connect this gateway to NervePay Mission Control? [y/N] y
|
|
139
|
-
Gateway name: Home Lab Gateway
|
|
140
|
-
Gateway URL: http://127.0.0.1:18789
|
|
141
|
-
|
|
142
|
-
✅ Gateway paired successfully!
|
|
143
|
-
|
|
144
|
-
⚙️ Step 5: Additional Features
|
|
145
|
-
Enable multi-agent orchestration? [y/N] n
|
|
146
|
-
Enable analytics tracking? [Y/n] y
|
|
147
|
-
|
|
148
|
-
✅ Setup Complete!
|
|
149
|
-
|
|
150
|
-
🔄 Next Steps:
|
|
151
|
-
1. Restart your OpenClaw gateway:
|
|
152
|
-
openclaw gateway restart
|
|
153
|
-
|
|
154
|
-
2. Test authentication:
|
|
155
|
-
openclaw nervepay whoami
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### 🛠️ The Manual Way (Programmatic)
|
|
159
|
-
|
|
160
|
-
If you prefer to use tools directly:
|
|
161
|
-
|
|
162
|
-
```javascript
|
|
163
|
-
// 1. Register identity
|
|
164
|
-
const result = await nervepay_register_identity({
|
|
165
|
-
name: "My AI Agent",
|
|
166
|
-
description: "Purpose of this agent"
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// 2. Save credentials (shown once only!)
|
|
170
|
-
console.log("DID:", result.did);
|
|
171
|
-
console.log("Private Key:", result.private_key);
|
|
172
|
-
console.log("Mnemonic:", result.mnemonic);
|
|
173
|
-
|
|
174
|
-
// 3. Update OpenClaw config with credentials
|
|
175
|
-
// 4. Restart gateway
|
|
176
|
-
// 5. Test authentication
|
|
177
|
-
const info = await nervepay_whoami();
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
**⚠️ Setup wizard is recommended!** It handles all the tedious configuration automatically.
|
|
181
|
-
|
|
182
|
-
## Available Tools
|
|
183
|
-
|
|
184
|
-
### Identity Tools
|
|
185
|
-
|
|
186
|
-
#### `nervepay_register_identity`
|
|
187
|
-
|
|
188
|
-
Register a new agent identity with W3C DID and Ed25519 keys.
|
|
189
|
-
|
|
190
|
-
**Parameters:**
|
|
191
|
-
- `name` (string, required): Agent display name
|
|
192
|
-
- `description` (string, optional): Agent description/purpose
|
|
193
|
-
|
|
194
|
-
**Returns:**
|
|
195
|
-
```typescript
|
|
196
|
-
{
|
|
197
|
-
did: string; // did:nervepay:agent:...
|
|
198
|
-
private_key: string; // ed25519:...
|
|
199
|
-
public_key: string; // Base58-encoded
|
|
200
|
-
mnemonic: string; // 12-word BIP39 recovery phrase
|
|
201
|
-
session_id: string; // For claim status polling
|
|
202
|
-
claim_url: string; // Give to human owner (optional)
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
#### `nervepay_whoami`
|
|
207
|
-
|
|
208
|
-
Test authentication and get current agent info.
|
|
209
|
-
|
|
210
|
-
**Returns:**
|
|
211
|
-
```typescript
|
|
212
|
-
{
|
|
213
|
-
agent_did: string;
|
|
214
|
-
name: string;
|
|
215
|
-
public_key: string;
|
|
216
|
-
capabilities: {
|
|
217
|
-
max_spending_limit_usd?: number;
|
|
218
|
-
allowed_operations?: string[];
|
|
219
|
-
};
|
|
220
|
-
reputation_score: number; // 0-100
|
|
221
|
-
created_at: string;
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
#### `nervepay_verify_agent`
|
|
226
|
-
|
|
227
|
-
Verify another agent's identity (public endpoint, no auth required).
|
|
228
|
-
|
|
229
|
-
**Parameters:**
|
|
230
|
-
- `did` (string): Agent DID to verify
|
|
231
|
-
|
|
232
|
-
#### `nervepay_resolve_did`
|
|
233
|
-
|
|
234
|
-
Resolve W3C DID document.
|
|
235
|
-
|
|
236
|
-
**Parameters:**
|
|
237
|
-
- `did` (string): DID to resolve
|
|
238
|
-
|
|
239
|
-
### Gateway Tools
|
|
240
|
-
|
|
241
|
-
#### `nervepay_pair_gateway`
|
|
242
|
-
|
|
243
|
-
Connect OpenClaw gateway to NervePay. Sends pairing request that requires human approval.
|
|
244
|
-
|
|
245
|
-
**Parameters:**
|
|
246
|
-
- `gateway_name` (string): Gateway display name
|
|
247
|
-
- `gateway_url` (string): Gateway URL (must be reachable by NervePay)
|
|
248
|
-
- `max_concurrent_agents` (number, optional): Max concurrent sub-agents (default: 8)
|
|
249
|
-
- `default_timeout_seconds` (number, optional): Default task timeout (default: 3600)
|
|
250
|
-
|
|
251
|
-
#### `nervepay_list_gateways`
|
|
252
|
-
|
|
253
|
-
List all connected gateways.
|
|
254
|
-
|
|
255
|
-
#### `nervepay_gateway_health`
|
|
256
|
-
|
|
257
|
-
Check gateway health.
|
|
258
|
-
|
|
259
|
-
**Parameters:**
|
|
260
|
-
- `gateway_id` (string): Gateway ID
|
|
261
|
-
|
|
262
|
-
### Vault Tools
|
|
263
|
-
|
|
264
|
-
#### `nervepay_list_secrets`
|
|
265
|
-
|
|
266
|
-
List all secrets (metadata only, no values).
|
|
267
|
-
|
|
268
|
-
**Returns:**
|
|
269
|
-
```typescript
|
|
270
|
-
{
|
|
271
|
-
secrets: Array<{
|
|
272
|
-
id: string;
|
|
273
|
-
name: string;
|
|
274
|
-
description?: string;
|
|
275
|
-
created_at: string;
|
|
276
|
-
updated_at: string;
|
|
277
|
-
}>;
|
|
278
|
-
}
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
#### `nervepay_get_secret`
|
|
282
|
-
|
|
283
|
-
Get decrypted secret value by name.
|
|
284
|
-
|
|
285
|
-
**Parameters:**
|
|
286
|
-
- `secret_name` (string): Secret name
|
|
287
|
-
|
|
288
|
-
**Returns:**
|
|
289
|
-
```typescript
|
|
290
|
-
{
|
|
291
|
-
secret: {
|
|
292
|
-
id: string;
|
|
293
|
-
name: string;
|
|
294
|
-
value: string; // Decrypted value
|
|
295
|
-
description?: string;
|
|
296
|
-
created_at: string;
|
|
297
|
-
updated_at: string;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
#### `nervepay_get_secrets`
|
|
303
|
-
|
|
304
|
-
Get multiple secrets at once.
|
|
305
|
-
|
|
306
|
-
**Parameters:**
|
|
307
|
-
- `secret_names` (string[]): Array of secret names
|
|
308
|
-
|
|
309
|
-
**Returns:**
|
|
310
|
-
```typescript
|
|
311
|
-
Record<string, string> // { name: value, ... }
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
### Orchestration Tools
|
|
315
|
-
|
|
316
|
-
(Available when `enableOrchestration: true`)
|
|
317
|
-
|
|
318
|
-
#### `nervepay_create_orchestration`
|
|
319
|
-
|
|
320
|
-
Create multi-agent orchestration from PRD (Product Requirements Document).
|
|
321
|
-
|
|
322
|
-
**Parameters:**
|
|
323
|
-
- `gateway_id` (string): Gateway ID to spawn agents on
|
|
324
|
-
- `name` (string): Orchestration name
|
|
325
|
-
- `prd` (string): Product Requirements Document (JSON or text)
|
|
326
|
-
- `context` (object, optional): Additional context data
|
|
327
|
-
|
|
328
|
-
#### `nervepay_start_orchestration`
|
|
329
|
-
|
|
330
|
-
Start orchestration (decomposes PRD and spawns sub-agent tasks).
|
|
331
|
-
|
|
332
|
-
**Parameters:**
|
|
333
|
-
- `orchestration_id` (string): Orchestration ID
|
|
334
|
-
|
|
335
|
-
#### `nervepay_get_orchestration`
|
|
336
|
-
|
|
337
|
-
Get orchestration status and progress.
|
|
338
|
-
|
|
339
|
-
**Parameters:**
|
|
340
|
-
- `orchestration_id` (string): Orchestration ID
|
|
341
|
-
|
|
342
|
-
#### `nervepay_list_tasks`
|
|
343
|
-
|
|
344
|
-
List tasks for an orchestration.
|
|
345
|
-
|
|
346
|
-
**Parameters:**
|
|
347
|
-
- `orchestration_id` (string): Orchestration ID
|
|
348
|
-
- `status` (string, optional): Filter by status (pending/running/completed/failed/blocked)
|
|
349
|
-
|
|
350
|
-
## CLI Commands
|
|
351
|
-
|
|
352
|
-
```bash
|
|
353
|
-
# Interactive setup wizard (⭐ recommended for first-time setup)
|
|
354
|
-
openclaw nervepay setup
|
|
355
|
-
|
|
356
|
-
# Show agent identity
|
|
357
|
-
openclaw nervepay whoami
|
|
358
|
-
|
|
359
|
-
# List connected gateways
|
|
360
|
-
openclaw nervepay gateways
|
|
361
|
-
|
|
362
|
-
# List secrets
|
|
363
|
-
openclaw nervepay secrets
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
The `setup` command is **fully interactive** and handles everything automatically:
|
|
367
|
-
- Identity registration
|
|
368
|
-
- Human claim flow
|
|
369
|
-
- Gateway pairing
|
|
370
|
-
- OpenClaw config updates
|
|
371
|
-
- Credentials backup
|
|
65
|
+
Restart gateway after changes: `openclaw gateway restart`
|
|
372
66
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
```javascript
|
|
376
|
-
// Health check
|
|
377
|
-
gateway.rpc('nervepay.health');
|
|
378
|
-
// => { status: 'ok', plugin: 'nervepay', version: '1.0.0' }
|
|
379
|
-
|
|
380
|
-
// Get agent identity
|
|
381
|
-
gateway.rpc('nervepay.whoami');
|
|
382
|
-
// => { agent_did: '...', name: '...', ... }
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
## Auto-Reply Commands
|
|
386
|
-
|
|
387
|
-
```
|
|
388
|
-
/nervepay-status
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
Shows plugin status and configuration in chat.
|
|
67
|
+
</details>
|
|
392
68
|
|
|
393
69
|
## Security
|
|
394
70
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
All authenticated requests use Ed25519 signatures with:
|
|
398
|
-
- **Replay protection**: One-time nonces + 5-minute timestamp window
|
|
399
|
-
- **Integrity**: Signature covers method, path, query, body hash
|
|
400
|
-
- **Non-repudiation**: Only agent with private key can sign
|
|
401
|
-
|
|
402
|
-
### Vault Encryption
|
|
403
|
-
|
|
404
|
-
Secrets use AES-256-GCM envelope encryption:
|
|
405
|
-
- Per-secret Data Encryption Keys (DEK)
|
|
406
|
-
- Master Key Encryption Key (KEK) derived via HKDF-SHA256
|
|
407
|
-
- Random 12-byte nonces per encryption
|
|
408
|
-
- Comprehensive audit logging
|
|
409
|
-
|
|
410
|
-
### Private Key Storage
|
|
411
|
-
|
|
412
|
-
**DO NOT** commit private keys to git or share them. Store them in:
|
|
413
|
-
- Environment variables
|
|
414
|
-
- OpenClaw config (local only)
|
|
415
|
-
- Hardware security modules (HSM)
|
|
416
|
-
- Secure secret managers
|
|
417
|
-
|
|
418
|
-
**BIP39 Mnemonic**: Backup the 12-word recovery phrase offline. It can regenerate the same Ed25519 keypair deterministically.
|
|
419
|
-
|
|
420
|
-
## Use Cases
|
|
421
|
-
|
|
422
|
-
### 1. Secure API Key Management
|
|
71
|
+
**Authentication:** All requests signed with Ed25519 — replay protection via one-time nonces + 5-minute timestamp window. Signatures cover method, path, query, and body hash.
|
|
423
72
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
```javascript
|
|
427
|
-
// Instead of this (insecure):
|
|
428
|
-
const apiKey = "sk-proj-..."; // Leaked in skill code!
|
|
429
|
-
|
|
430
|
-
// Do this:
|
|
431
|
-
const { secret } = await nervepay_get_secret({
|
|
432
|
-
secret_name: "openai_api_key"
|
|
433
|
-
});
|
|
434
|
-
const apiKey = secret.value;
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
**Snyk found 280+ OpenClaw skills leaking API keys.** NervePay Vault fixes this with encrypted storage and audit logs.
|
|
438
|
-
|
|
439
|
-
### 2. Cross-Platform Reputation
|
|
440
|
-
|
|
441
|
-
Your agent's DID carries reputation across all 30+ OpenClaw channels:
|
|
442
|
-
|
|
443
|
-
```javascript
|
|
444
|
-
// Verify agent before trusting
|
|
445
|
-
const { verified, agent } = await nervepay_verify_agent({
|
|
446
|
-
did: "did:nervepay:agent:abc123..."
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
if (verified && agent.reputation_score > 80) {
|
|
450
|
-
// High reputation, proceed with request
|
|
451
|
-
}
|
|
452
|
-
```
|
|
73
|
+
**Vault:** AES-256-GCM envelope encryption with per-secret DEKs wrapped by a master KEK (HKDF-SHA256). All access audit-logged.
|
|
453
74
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
Decompose complex PRDs into sub-agent tasks:
|
|
457
|
-
|
|
458
|
-
```javascript
|
|
459
|
-
// Create orchestration
|
|
460
|
-
const orch = await nervepay_create_orchestration({
|
|
461
|
-
gateway_id: "gateway-123",
|
|
462
|
-
name: "Build User Dashboard",
|
|
463
|
-
prd: JSON.stringify({
|
|
464
|
-
title: "User Dashboard",
|
|
465
|
-
requirements: [
|
|
466
|
-
"Design landing page layout",
|
|
467
|
-
"Implement authentication",
|
|
468
|
-
"Create user profile page",
|
|
469
|
-
"Add analytics charts"
|
|
470
|
-
]
|
|
471
|
-
})
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
// Start execution
|
|
475
|
-
await nervepay_start_orchestration({
|
|
476
|
-
orchestration_id: orch.id
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
// Monitor progress
|
|
480
|
-
const status = await nervepay_get_orchestration({
|
|
481
|
-
orchestration_id: orch.id
|
|
482
|
-
});
|
|
483
|
-
console.log(`Progress: ${status.progress_percentage}%`);
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
### 4. Agent-to-Agent Commerce
|
|
487
|
-
|
|
488
|
-
Coming soon: x402 protocol integration for pay-per-request API monetization.
|
|
75
|
+
**Key Storage:** Private keys live in local config only. BIP39 mnemonic shown once during setup — back it up offline for recovery.
|
|
489
76
|
|
|
490
77
|
## Development
|
|
491
78
|
|
|
492
|
-
### Build
|
|
493
|
-
|
|
494
79
|
```bash
|
|
495
80
|
npm install
|
|
496
81
|
npm run build
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
### Test
|
|
500
|
-
|
|
501
|
-
```bash
|
|
502
82
|
npm test
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
### Watch mode
|
|
506
|
-
|
|
507
|
-
```bash
|
|
508
|
-
npm run dev
|
|
83
|
+
npm run dev # watch mode
|
|
509
84
|
```
|
|
510
85
|
|
|
511
86
|
## Links
|
|
512
87
|
|
|
513
|
-
-
|
|
514
|
-
-
|
|
515
|
-
-
|
|
516
|
-
-
|
|
517
|
-
-
|
|
518
|
-
-
|
|
519
|
-
|
|
520
|
-
## Support
|
|
521
|
-
|
|
522
|
-
- **Issues**: https://github.com/nervepay/nervepay-plugin/issues
|
|
523
|
-
- **Discord**: https://discord.gg/nervepay
|
|
524
|
-
- **Email**: hello@nervepay.xyz
|
|
88
|
+
- [Website](https://nervepay.xyz)
|
|
89
|
+
- [Dashboard](https://nervepay.xyz/dashboard)
|
|
90
|
+
- [Docs](https://nervepay.xyz/docs)
|
|
91
|
+
- [API Reference](https://nervepay.xyz/docs/api)
|
|
92
|
+
- [GitHub](https://github.com/nervepay/nervepay-plugin)
|
|
93
|
+
- [Issues](https://github.com/nervepay/nervepay-plugin/issues)
|
|
94
|
+
- [Discord](https://discord.gg/nervepay)
|
|
525
95
|
|
|
526
96
|
## License
|
|
527
97
|
|
|
528
|
-
MIT
|
|
529
|
-
|
|
530
|
-
---
|
|
531
|
-
|
|
532
|
-
**Built with ❤️ for the AI agent ecosystem**
|
|
98
|
+
MIT
|
package/bin/nervepay-cli.js
CHANGED
|
@@ -176,6 +176,38 @@ async function updateOpenClawConfig(agentDid, privateKey, apiUrl) {
|
|
|
176
176
|
} catch { return false; }
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Enable /v1/chat/completions on the OpenClaw gateway.
|
|
181
|
+
*
|
|
182
|
+
* Sets gateway.http.endpoints.chatCompletions.enabled = true in openclaw.json.
|
|
183
|
+
* This allows NervePay Mission Control to chat directly without spawning sub-agents.
|
|
184
|
+
*
|
|
185
|
+
* Returns true if config was updated, false otherwise.
|
|
186
|
+
* The gateway must be restarted for the change to take effect.
|
|
187
|
+
*/
|
|
188
|
+
async function enableChatCompletions() {
|
|
189
|
+
const configPath = findOpenClawConfigPath();
|
|
190
|
+
if (!configPath) return false;
|
|
191
|
+
try {
|
|
192
|
+
const config = JSON.parse(await readFile(configPath, 'utf-8'));
|
|
193
|
+
|
|
194
|
+
// Check if already enabled
|
|
195
|
+
if (config.gateway?.http?.endpoints?.chatCompletions?.enabled === true) {
|
|
196
|
+
return 'already_enabled';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Deep-merge the setting
|
|
200
|
+
if (!config.gateway) config.gateway = {};
|
|
201
|
+
if (!config.gateway.http) config.gateway.http = {};
|
|
202
|
+
if (!config.gateway.http.endpoints) config.gateway.http.endpoints = {};
|
|
203
|
+
if (!config.gateway.http.endpoints.chatCompletions) config.gateway.http.endpoints.chatCompletions = {};
|
|
204
|
+
config.gateway.http.endpoints.chatCompletions.enabled = true;
|
|
205
|
+
|
|
206
|
+
await writeFile(configPath, JSON.stringify(config, null, 2));
|
|
207
|
+
return true;
|
|
208
|
+
} catch { return false; }
|
|
209
|
+
}
|
|
210
|
+
|
|
179
211
|
// ---------------------------------------------------------------------------
|
|
180
212
|
// CLI program
|
|
181
213
|
// ---------------------------------------------------------------------------
|
|
@@ -277,98 +309,149 @@ program
|
|
|
277
309
|
log(dim('You can pair later with:'), info('nervepay pair'));
|
|
278
310
|
}
|
|
279
311
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
312
|
+
// Check for existing identity (same check as `pair` command)
|
|
313
|
+
let agentDid, privateKey, mnemonic, apiUrl;
|
|
314
|
+
let isExistingIdentity = false;
|
|
315
|
+
try {
|
|
316
|
+
const existing = await loadConfig();
|
|
317
|
+
logOk('Existing agent identity found');
|
|
318
|
+
field(' DID', existing.agentDid);
|
|
319
|
+
console.log();
|
|
285
320
|
|
|
286
|
-
|
|
287
|
-
|
|
321
|
+
const { reuse } = await inquirer.prompt([{
|
|
322
|
+
type: 'confirm',
|
|
323
|
+
name: 'reuse',
|
|
324
|
+
message: 'Use existing identity?',
|
|
325
|
+
default: true,
|
|
326
|
+
}]);
|
|
327
|
+
|
|
328
|
+
if (reuse) {
|
|
329
|
+
agentDid = existing.agentDid;
|
|
330
|
+
privateKey = existing.privateKey;
|
|
331
|
+
apiUrl = existing.apiUrl || options.apiUrl;
|
|
332
|
+
isExistingIdentity = true;
|
|
333
|
+
}
|
|
334
|
+
} catch {
|
|
335
|
+
// No existing credentials — will create new
|
|
336
|
+
}
|
|
288
337
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
338
|
+
if (!isExistingIdentity) {
|
|
339
|
+
console.log();
|
|
340
|
+
const answers = await inquirer.prompt([
|
|
341
|
+
{ type: 'input', name: 'name', message: 'Agent name:', default: 'My AI Agent' },
|
|
342
|
+
{ type: 'input', name: 'description', message: 'Description:', default: 'AI agent with self-sovereign identity' },
|
|
343
|
+
]);
|
|
294
344
|
|
|
295
|
-
|
|
296
|
-
|
|
345
|
+
console.log();
|
|
346
|
+
logInfo('Registering agent identity...');
|
|
297
347
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
348
|
+
const client = new NervePayClient({ apiUrl: options.apiUrl });
|
|
349
|
+
const reg = await identity.registerPendingIdentity(client, {
|
|
350
|
+
name: answers.name,
|
|
351
|
+
description: answers.description,
|
|
352
|
+
});
|
|
302
353
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
} catch {
|
|
308
|
-
log(dim('Open the URL above in your browser'));
|
|
309
|
-
}
|
|
354
|
+
agentDid = reg.did;
|
|
355
|
+
privateKey = reg.private_key;
|
|
356
|
+
mnemonic = reg.mnemonic;
|
|
357
|
+
apiUrl = options.apiUrl;
|
|
310
358
|
|
|
311
|
-
|
|
312
|
-
|
|
359
|
+
logOk('Agent identity created');
|
|
360
|
+
field(' DID', agentDid);
|
|
361
|
+
|
|
362
|
+
console.log();
|
|
363
|
+
logInfo('Claim this agent in your browser:');
|
|
364
|
+
console.log(bold(` ${reg.claim_url}`));
|
|
365
|
+
console.log();
|
|
313
366
|
|
|
314
|
-
let claimed = false;
|
|
315
|
-
for (let i = 0; i < 60; i++) {
|
|
316
|
-
await new Promise((r) => setTimeout(r, 5000));
|
|
317
367
|
try {
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
}
|
|
368
|
+
const open = await import('open');
|
|
369
|
+
await open.default(reg.claim_url);
|
|
370
|
+
log(dim('Opened in browser'));
|
|
371
|
+
} catch {
|
|
372
|
+
log(dim('Open the URL above in your browser'));
|
|
373
|
+
}
|
|
325
374
|
|
|
326
|
-
if (claimed) {
|
|
327
|
-
console.log();
|
|
328
|
-
logOk('Agent claimed');
|
|
329
|
-
} else {
|
|
330
375
|
console.log();
|
|
331
|
-
|
|
332
|
-
log(dim(reg.claim_url));
|
|
333
|
-
}
|
|
376
|
+
logInfo('Waiting for claim...');
|
|
334
377
|
|
|
335
|
-
|
|
378
|
+
let claimed = false;
|
|
379
|
+
for (let i = 0; i < 60; i++) {
|
|
380
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
381
|
+
try {
|
|
382
|
+
const resp = await fetch(`${apiUrl}/v1/agent-identity/register-pending/${reg.session_id}/status`);
|
|
383
|
+
const data = await resp.json();
|
|
384
|
+
if (data.status === 'claimed') { claimed = true; break; }
|
|
385
|
+
if (data.status === 'expired') { break; }
|
|
386
|
+
process.stdout.write(dim('.'));
|
|
387
|
+
} catch { process.stdout.write(dim('.')); }
|
|
388
|
+
}
|
|
336
389
|
|
|
337
|
-
|
|
338
|
-
|
|
390
|
+
if (claimed) {
|
|
391
|
+
console.log();
|
|
392
|
+
logOk('Agent claimed');
|
|
393
|
+
} else {
|
|
394
|
+
console.log();
|
|
395
|
+
logWarn('Not claimed yet — you can claim later');
|
|
396
|
+
log(dim(reg.claim_url));
|
|
397
|
+
}
|
|
339
398
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
399
|
+
await saveCredentials(agentDid, privateKey, mnemonic, apiUrl);
|
|
400
|
+
logOk('Credentials saved');
|
|
401
|
+
|
|
402
|
+
if (configPath) {
|
|
403
|
+
if (await updateOpenClawConfig(agentDid, privateKey, apiUrl)) {
|
|
404
|
+
logOk('OpenClaw config updated');
|
|
405
|
+
}
|
|
343
406
|
}
|
|
344
407
|
}
|
|
345
408
|
|
|
409
|
+
const client = new NervePayClient({ apiUrl, agentDid, privateKey });
|
|
410
|
+
|
|
411
|
+
// Gateway pairing via device node protocol
|
|
412
|
+
let paired = false;
|
|
346
413
|
if (gatewayUrl && gatewayToken) {
|
|
347
414
|
console.log();
|
|
348
|
-
logInfo('
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
415
|
+
logInfo('Pairing gateway via device node protocol...');
|
|
416
|
+
try {
|
|
417
|
+
await performDeviceNodePairing({
|
|
418
|
+
agentDid,
|
|
419
|
+
privateKey,
|
|
420
|
+
apiUrl,
|
|
421
|
+
gatewayUrl,
|
|
422
|
+
gatewayToken,
|
|
423
|
+
gatewayName: 'OpenClaw Gateway',
|
|
424
|
+
timeoutSeconds: 300,
|
|
425
|
+
});
|
|
426
|
+
paired = true;
|
|
427
|
+
} catch (pairErr) {
|
|
428
|
+
logWarn(`Gateway pairing skipped: ${pairErr.message}`);
|
|
429
|
+
log(dim('Pair later with:'), info('nervepay pair'));
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Enable /v1/chat/completions for direct chat (no sub-agent spawning)
|
|
434
|
+
const chatResult = await enableChatCompletions();
|
|
435
|
+
if (chatResult === true) {
|
|
436
|
+
logOk('Enabled chat completions endpoint');
|
|
437
|
+
log(dim(' Restart gateway for this to take effect:'), info('openclaw gateway restart'));
|
|
357
438
|
}
|
|
358
439
|
|
|
359
440
|
divider();
|
|
360
441
|
logOk(bold('Setup complete'));
|
|
361
442
|
console.log();
|
|
362
443
|
|
|
363
|
-
if (!gatewayUrl) {
|
|
364
|
-
log(dim('Next:'), info('nervepay pair
|
|
444
|
+
if (!gatewayUrl || !paired) {
|
|
445
|
+
log(dim('Next:'), info('nervepay pair'));
|
|
365
446
|
} else {
|
|
366
447
|
log(dim('Next:'), info('nervepay whoami'));
|
|
367
448
|
}
|
|
368
449
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
450
|
+
if (mnemonic) {
|
|
451
|
+
console.log();
|
|
452
|
+
log(warn('Recovery mnemonic (save this!):'));
|
|
453
|
+
console.log(bold(` ${mnemonic}`));
|
|
454
|
+
}
|
|
372
455
|
console.log();
|
|
373
456
|
} catch (error) {
|
|
374
457
|
die(`Setup failed: ${error.message}`);
|
|
@@ -472,13 +555,101 @@ function attemptConnect(WebSocket, wsUrl, ctx) {
|
|
|
472
555
|
}
|
|
473
556
|
|
|
474
557
|
// ---------------------------------------------------------------------------
|
|
475
|
-
//
|
|
558
|
+
// Shared device node pairing logic
|
|
476
559
|
// ---------------------------------------------------------------------------
|
|
477
560
|
|
|
478
|
-
|
|
561
|
+
/**
|
|
562
|
+
* Perform WebSocket device node pairing with the OpenClaw gateway.
|
|
563
|
+
*
|
|
564
|
+
* Connects as a device node, handles the challenge-response auth, and polls
|
|
565
|
+
* for gateway owner approval if the device isn't already paired.
|
|
566
|
+
*
|
|
567
|
+
* Returns { gateway_id, device_id } on success, throws on failure.
|
|
568
|
+
*/
|
|
569
|
+
async function performDeviceNodePairing({ agentDid, privateKey, apiUrl, gatewayUrl, gatewayToken, gatewayName, timeoutSeconds = 300 }) {
|
|
479
570
|
const { signRawBytes, deriveDeviceId, getPublicKeyFromPrivate, decodeBase58 } =
|
|
480
571
|
await import('../dist/utils/crypto.js');
|
|
481
572
|
|
|
573
|
+
const wsUrl = httpToWs(gatewayUrl);
|
|
574
|
+
field(' Gateway', wsUrl);
|
|
575
|
+
|
|
576
|
+
// Device identity
|
|
577
|
+
const publicKeyBase58 = await getPublicKeyFromPrivate(privateKey);
|
|
578
|
+
const deviceId = deriveDeviceId(publicKeyBase58);
|
|
579
|
+
field(' Device', deviceId.slice(0, 16) + '...');
|
|
580
|
+
|
|
581
|
+
console.log();
|
|
582
|
+
logInfo('Connecting...');
|
|
583
|
+
|
|
584
|
+
const WebSocket = (await import('ws')).default;
|
|
585
|
+
const pubKeyBytes = decodeBase58(publicKeyBase58);
|
|
586
|
+
const pubKeyB64Url = Buffer.from(pubKeyBytes).toString('base64')
|
|
587
|
+
.replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '');
|
|
588
|
+
|
|
589
|
+
const ctx = { deviceId, pubKeyB64Url, privateKey, gatewayToken, signRawBytes, VERSION };
|
|
590
|
+
const timeoutMs = timeoutSeconds * 1000;
|
|
591
|
+
|
|
592
|
+
// First attempt
|
|
593
|
+
try {
|
|
594
|
+
await attemptConnect(WebSocket, wsUrl, ctx);
|
|
595
|
+
logOk('Connected (device already paired)');
|
|
596
|
+
} catch (e) {
|
|
597
|
+
if (e.message !== 'NOT_PAIRED') throw e;
|
|
598
|
+
|
|
599
|
+
// Device needs pairing approval — poll reconnect until approved
|
|
600
|
+
logOk('Pairing request created');
|
|
601
|
+
console.log();
|
|
602
|
+
logInfo('Waiting for gateway owner approval...');
|
|
603
|
+
log(dim(' Approve on gateway: openclaw devices approve'));
|
|
604
|
+
log(dim(` Timeout: ${timeoutSeconds}s`));
|
|
605
|
+
console.log();
|
|
606
|
+
|
|
607
|
+
const pollInterval = 5000;
|
|
608
|
+
const maxAttempts = Math.ceil(timeoutMs / pollInterval);
|
|
609
|
+
let approved = false;
|
|
610
|
+
|
|
611
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
612
|
+
await new Promise(r => setTimeout(r, pollInterval));
|
|
613
|
+
process.stdout.write(dim('.'));
|
|
614
|
+
|
|
615
|
+
try {
|
|
616
|
+
await attemptConnect(WebSocket, wsUrl, ctx);
|
|
617
|
+
approved = true;
|
|
618
|
+
console.log();
|
|
619
|
+
break;
|
|
620
|
+
} catch (retryErr) {
|
|
621
|
+
if (retryErr.message !== 'NOT_PAIRED') {
|
|
622
|
+
console.log();
|
|
623
|
+
throw retryErr;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (!approved) throw new Error(`Approval timed out after ${timeoutSeconds}s`);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
logOk(bold('Paired!'));
|
|
631
|
+
console.log();
|
|
632
|
+
|
|
633
|
+
// Store the master gateway token (used for REST API calls by the backend).
|
|
634
|
+
// The device token from hello-ok is WS-only; the backend uses HTTP Bearer auth.
|
|
635
|
+
logInfo('Saving to NervePay...');
|
|
636
|
+
const client = new NervePayClient({ apiUrl, agentDid, privateKey });
|
|
637
|
+
|
|
638
|
+
const apiResult = await gateway.completeDevicePairing(client, {
|
|
639
|
+
device_token: gatewayToken,
|
|
640
|
+
device_id: deviceId,
|
|
641
|
+
gateway_url: gatewayUrl,
|
|
642
|
+
gateway_name: gatewayName || 'OpenClaw Gateway',
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
return { gateway_id: apiResult.gateway_id, device_id: deviceId };
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// ---------------------------------------------------------------------------
|
|
649
|
+
// Device node pairing (pair command)
|
|
650
|
+
// ---------------------------------------------------------------------------
|
|
651
|
+
|
|
652
|
+
async function deviceNodePairing(options) {
|
|
482
653
|
logInfo('Device node pairing');
|
|
483
654
|
console.log();
|
|
484
655
|
|
|
@@ -503,87 +674,32 @@ async function deviceNodePairing(options) {
|
|
|
503
674
|
if (!gatewayUrl) die('No gateway URL found', 'Use: nervepay pair --gateway-url <url>');
|
|
504
675
|
if (!gatewayToken) die('No gateway token found', 'Use: nervepay pair --gateway-token <token>');
|
|
505
676
|
|
|
506
|
-
const
|
|
507
|
-
field(' Gateway', wsUrl);
|
|
508
|
-
|
|
509
|
-
// Device identity
|
|
510
|
-
const publicKeyBase58 = await getPublicKeyFromPrivate(privateKey);
|
|
511
|
-
const deviceId = deriveDeviceId(publicKeyBase58);
|
|
512
|
-
field(' Device', deviceId.slice(0, 16) + '...');
|
|
513
|
-
|
|
514
|
-
console.log();
|
|
515
|
-
logInfo('Connecting...');
|
|
516
|
-
|
|
517
|
-
const WebSocket = (await import('ws')).default;
|
|
518
|
-
const pubKeyBytes = decodeBase58(publicKeyBase58);
|
|
519
|
-
const pubKeyB64Url = Buffer.from(pubKeyBytes).toString('base64')
|
|
520
|
-
.replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '');
|
|
521
|
-
|
|
522
|
-
const ctx = { deviceId, pubKeyB64Url, privateKey, gatewayToken, signRawBytes, VERSION };
|
|
523
|
-
const timeoutMs = parseInt(options.timeout, 10) * 1000;
|
|
524
|
-
|
|
525
|
-
// First attempt
|
|
526
|
-
try {
|
|
527
|
-
await attemptConnect(WebSocket, wsUrl, ctx);
|
|
528
|
-
logOk('Connected (device already paired)');
|
|
529
|
-
} catch (e) {
|
|
530
|
-
if (e.message !== 'NOT_PAIRED') throw e;
|
|
531
|
-
|
|
532
|
-
// Device needs pairing approval — poll reconnect until approved
|
|
533
|
-
logOk('Pairing request created');
|
|
534
|
-
console.log();
|
|
535
|
-
logInfo('Waiting for gateway owner approval...');
|
|
536
|
-
log(dim(' Approve on gateway: openclaw devices approve'));
|
|
537
|
-
log(dim(` Timeout: ${options.timeout}s`));
|
|
538
|
-
console.log();
|
|
539
|
-
|
|
540
|
-
const pollInterval = 5000;
|
|
541
|
-
const maxAttempts = Math.ceil(timeoutMs / pollInterval);
|
|
542
|
-
let approved = false;
|
|
543
|
-
|
|
544
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
545
|
-
await new Promise(r => setTimeout(r, pollInterval));
|
|
546
|
-
process.stdout.write(dim('.'));
|
|
547
|
-
|
|
548
|
-
try {
|
|
549
|
-
await attemptConnect(WebSocket, wsUrl, ctx);
|
|
550
|
-
approved = true;
|
|
551
|
-
console.log();
|
|
552
|
-
break;
|
|
553
|
-
} catch (retryErr) {
|
|
554
|
-
if (retryErr.message !== 'NOT_PAIRED') {
|
|
555
|
-
console.log();
|
|
556
|
-
throw retryErr;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
if (!approved) die(`Approval timed out after ${options.timeout}s`);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
logOk(bold('Paired!'));
|
|
564
|
-
console.log();
|
|
565
|
-
|
|
566
|
-
// Store the master gateway token (used for REST API calls by the backend).
|
|
567
|
-
// The device token from hello-ok is WS-only; the backend uses HTTP Bearer auth.
|
|
568
|
-
logInfo('Saving to NervePay...');
|
|
569
|
-
const client = new NervePayClient({
|
|
570
|
-
apiUrl: options.apiUrl || apiUrl,
|
|
677
|
+
const result = await performDeviceNodePairing({
|
|
571
678
|
agentDid,
|
|
572
679
|
privateKey,
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
gateway_url: gatewayUrl,
|
|
579
|
-
gateway_name: options.name,
|
|
680
|
+
apiUrl: options.apiUrl || apiUrl,
|
|
681
|
+
gatewayUrl,
|
|
682
|
+
gatewayToken,
|
|
683
|
+
gatewayName: options.name,
|
|
684
|
+
timeoutSeconds: parseInt(options.timeout, 10),
|
|
580
685
|
});
|
|
581
686
|
|
|
582
687
|
divider();
|
|
583
688
|
logOk(bold('Gateway paired'));
|
|
584
|
-
field(' Gateway ID',
|
|
585
|
-
field(' Device ID',
|
|
689
|
+
field(' Gateway ID', result.gateway_id);
|
|
690
|
+
field(' Device ID', result.device_id.slice(0, 16) + '...');
|
|
586
691
|
field(' Agent DID', agentDid);
|
|
692
|
+
console.log();
|
|
693
|
+
|
|
694
|
+
// Enable /v1/chat/completions for direct chat (no sub-agent spawning)
|
|
695
|
+
const chatResult = await enableChatCompletions();
|
|
696
|
+
if (chatResult === true) {
|
|
697
|
+
logOk('Enabled chat completions endpoint');
|
|
698
|
+
log(dim(' Restart gateway for this to take effect:'), info('openclaw gateway restart'));
|
|
699
|
+
} else if (chatResult === 'already_enabled') {
|
|
700
|
+
logOk('Chat completions endpoint already enabled');
|
|
701
|
+
}
|
|
702
|
+
|
|
587
703
|
console.log();
|
|
588
704
|
log(dim('View in dashboard:'), info('Mission Control > Task Board'));
|
|
589
705
|
console.log();
|
|
@@ -704,6 +820,17 @@ async function legacyCodePairing(options) {
|
|
|
704
820
|
logOk(bold('Gateway paired'));
|
|
705
821
|
field(' Gateway ID', result.gateway_id || 'created');
|
|
706
822
|
field(' Agent DID', agentDid);
|
|
823
|
+
console.log();
|
|
824
|
+
|
|
825
|
+
// Enable /v1/chat/completions for direct chat (no sub-agent spawning)
|
|
826
|
+
const chatResult = await enableChatCompletions();
|
|
827
|
+
if (chatResult === true) {
|
|
828
|
+
logOk('Enabled chat completions endpoint');
|
|
829
|
+
log(dim(' Restart gateway for this to take effect:'), info('openclaw gateway restart'));
|
|
830
|
+
} else if (chatResult === 'already_enabled') {
|
|
831
|
+
logOk('Chat completions endpoint already enabled');
|
|
832
|
+
}
|
|
833
|
+
|
|
707
834
|
console.log();
|
|
708
835
|
log(dim('View in dashboard:'), info('Mission Control > Task Board'));
|
|
709
836
|
console.log();
|