nervepay 1.0.0 ā 1.0.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/bin/nervepay-cli.js +315 -0
- package/openclaw.plugin.json +19 -1
- package/package.json +10 -2
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* NervePay CLI - Standalone command-line tool
|
|
4
|
+
* Works independently of OpenClaw plugin system
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
8
|
+
import { homedir } from 'os';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { Command } from 'commander';
|
|
11
|
+
import inquirer from 'inquirer';
|
|
12
|
+
import chalk from 'chalk';
|
|
13
|
+
import { NervePayClient } from '../dist/utils/client.js';
|
|
14
|
+
import * as identity from '../dist/tools/identity.js';
|
|
15
|
+
import * as gateway from '../dist/tools/gateway.js';
|
|
16
|
+
import * as vault from '../dist/tools/vault.js';
|
|
17
|
+
|
|
18
|
+
const OPENCLAW_CONFIG_PATH = join(homedir(), '.openclaw', 'openclaw.json');
|
|
19
|
+
const NERVEPAY_CREDS_PATH = join(homedir(), '.nervepay', 'credentials.json');
|
|
20
|
+
|
|
21
|
+
const program = new Command();
|
|
22
|
+
|
|
23
|
+
program
|
|
24
|
+
.name('nervepay')
|
|
25
|
+
.description('NervePay CLI - Self-sovereign identity for AI agents')
|
|
26
|
+
.version('1.0.1');
|
|
27
|
+
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// SETUP COMMAND
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
program
|
|
33
|
+
.command('setup')
|
|
34
|
+
.description('Interactive setup wizard - register identity, pair gateway')
|
|
35
|
+
.option('--api-url <url>', 'NervePay API URL', 'https://api.nervepay.xyz')
|
|
36
|
+
.action(async (options) => {
|
|
37
|
+
console.log(chalk.cyan('\nš NervePay Setup Wizard\n'));
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// Read OpenClaw config
|
|
41
|
+
const openclawConfig = JSON.parse(await readFile(OPENCLAW_CONFIG_PATH, 'utf-8'));
|
|
42
|
+
const gatewayConfig = openclawConfig.gateway?.remote;
|
|
43
|
+
|
|
44
|
+
if (!gatewayConfig?.url || !gatewayConfig?.token) {
|
|
45
|
+
console.error(chalk.red('ā No OpenClaw gateway configured'));
|
|
46
|
+
console.error(chalk.yellow(' Run: openclaw configure'));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.log(chalk.green('ā Found OpenClaw gateway:'), gatewayConfig.url);
|
|
51
|
+
|
|
52
|
+
// Ask for agent details
|
|
53
|
+
const answers = await inquirer.prompt([
|
|
54
|
+
{
|
|
55
|
+
type: 'input',
|
|
56
|
+
name: 'name',
|
|
57
|
+
message: 'Agent name:',
|
|
58
|
+
default: 'My AI Agent',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
type: 'input',
|
|
62
|
+
name: 'description',
|
|
63
|
+
message: 'Agent description:',
|
|
64
|
+
default: 'AI agent with self-sovereign identity',
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
console.log(chalk.cyan('\nš Registering agent identity...'));
|
|
69
|
+
|
|
70
|
+
// Create client (without credentials initially)
|
|
71
|
+
const client = new NervePayClient({ apiUrl: options.apiUrl });
|
|
72
|
+
|
|
73
|
+
// Register pending identity
|
|
74
|
+
const registration = await identity.registerPendingIdentity(client, {
|
|
75
|
+
name: answers.name,
|
|
76
|
+
description: answers.description,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
console.log(chalk.green('ā Agent identity created'));
|
|
80
|
+
console.log(chalk.gray(' DID:'), registration.did);
|
|
81
|
+
console.log(chalk.gray(' Session ID:'), registration.session_id);
|
|
82
|
+
console.log(chalk.gray(' Claim URL:'), registration.claim_url);
|
|
83
|
+
|
|
84
|
+
console.log(chalk.cyan('\nš¤ Please claim this agent in your dashboard:'));
|
|
85
|
+
console.log(chalk.yellow(` ${registration.claim_url}`));
|
|
86
|
+
console.log(chalk.gray(' (Opens in browser automatically if possible)\n'));
|
|
87
|
+
|
|
88
|
+
// Try to open in browser
|
|
89
|
+
const open = await import('open');
|
|
90
|
+
await open.default(registration.claim_url).catch(() => {
|
|
91
|
+
console.log(chalk.gray(' (Could not open browser automatically)'));
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Poll for claim
|
|
95
|
+
console.log(chalk.cyan('ā³ Waiting for claim (checking every 5 seconds)...\n'));
|
|
96
|
+
|
|
97
|
+
let claimed = false;
|
|
98
|
+
const maxAttempts = 60; // 5 minutes
|
|
99
|
+
let attempts = 0;
|
|
100
|
+
|
|
101
|
+
while (!claimed && attempts < maxAttempts) {
|
|
102
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
103
|
+
attempts++;
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const status = await fetch(`${options.apiUrl}/v1/agent-identity/register-pending/${registration.session_id}/status`);
|
|
107
|
+
const data = await status.json();
|
|
108
|
+
|
|
109
|
+
if (data.status === 'claimed') {
|
|
110
|
+
claimed = true;
|
|
111
|
+
console.log(chalk.green('ā Agent claimed successfully!\n'));
|
|
112
|
+
} else if (data.status === 'expired') {
|
|
113
|
+
console.error(chalk.red('ā Claim session expired'));
|
|
114
|
+
process.exit(1);
|
|
115
|
+
} else {
|
|
116
|
+
process.stdout.write(chalk.gray('.'));
|
|
117
|
+
}
|
|
118
|
+
} catch (e) {
|
|
119
|
+
process.stdout.write(chalk.gray('.'));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!claimed) {
|
|
124
|
+
console.error(chalk.red('\nā Claim timeout - please try again'));
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Update client with credentials
|
|
129
|
+
client.updateConfig({
|
|
130
|
+
agentDid: registration.did,
|
|
131
|
+
privateKey: registration.private_key,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Save credentials
|
|
135
|
+
const credsDir = join(homedir(), '.nervepay');
|
|
136
|
+
await writeFile(
|
|
137
|
+
NERVEPAY_CREDS_PATH,
|
|
138
|
+
JSON.stringify(
|
|
139
|
+
{
|
|
140
|
+
agent_did: registration.did,
|
|
141
|
+
private_key: registration.private_key,
|
|
142
|
+
mnemonic: registration.mnemonic,
|
|
143
|
+
created_at: new Date().toISOString(),
|
|
144
|
+
},
|
|
145
|
+
null,
|
|
146
|
+
2
|
|
147
|
+
)
|
|
148
|
+
);
|
|
149
|
+
console.log(chalk.green('ā Credentials saved to ~/.nervepay/credentials.json'));
|
|
150
|
+
|
|
151
|
+
// Update OpenClaw config
|
|
152
|
+
if (!openclawConfig.plugins) openclawConfig.plugins = {};
|
|
153
|
+
if (!openclawConfig.plugins.entries) openclawConfig.plugins.entries = {};
|
|
154
|
+
if (!openclawConfig.plugins.entries.nervepay) {
|
|
155
|
+
openclawConfig.plugins.entries.nervepay = { enabled: true, config: {} };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
openclawConfig.plugins.entries.nervepay.config = {
|
|
159
|
+
...openclawConfig.plugins.entries.nervepay.config,
|
|
160
|
+
apiUrl: options.apiUrl,
|
|
161
|
+
agentDid: registration.did,
|
|
162
|
+
privateKey: registration.private_key,
|
|
163
|
+
enableOrchestration: true,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
await writeFile(OPENCLAW_CONFIG_PATH, JSON.stringify(openclawConfig, null, 2));
|
|
167
|
+
console.log(chalk.green('ā OpenClaw config updated'));
|
|
168
|
+
|
|
169
|
+
// Send pairing request
|
|
170
|
+
console.log(chalk.cyan('\nš Pairing gateway...'));
|
|
171
|
+
|
|
172
|
+
const pairingRequest = await gateway.createPairingRequest(client, {
|
|
173
|
+
gateway_name: 'OpenClaw Gateway',
|
|
174
|
+
gateway_url: gatewayConfig.url,
|
|
175
|
+
max_concurrent_agents: 8,
|
|
176
|
+
default_timeout_seconds: 3600,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
console.log(chalk.green('ā Pairing request sent'));
|
|
180
|
+
console.log(chalk.yellow(' Approve in Mission Control > Task Board > Incoming tab'));
|
|
181
|
+
|
|
182
|
+
console.log(chalk.cyan('\nā
Setup complete!'));
|
|
183
|
+
console.log(chalk.gray('\nNext steps:'));
|
|
184
|
+
console.log(chalk.gray(' 1. Approve the pairing request in Mission Control'));
|
|
185
|
+
console.log(chalk.gray(' 2. Restart OpenClaw gateway:'), chalk.cyan('openclaw gateway restart'));
|
|
186
|
+
console.log(chalk.gray(' 3. Test:'), chalk.cyan('nervepay whoami'));
|
|
187
|
+
console.log(chalk.gray('\n Mnemonic (backup phrase):'), chalk.yellow(registration.mnemonic));
|
|
188
|
+
console.log(chalk.red(' ā ļø Save this mnemonic - you can recover your keys with it!'));
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.error(chalk.red('\nā Setup failed:'), error.message);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// ============================================================================
|
|
196
|
+
// WHOAMI COMMAND
|
|
197
|
+
// ============================================================================
|
|
198
|
+
|
|
199
|
+
program
|
|
200
|
+
.command('whoami')
|
|
201
|
+
.description('Show current agent identity')
|
|
202
|
+
.action(async () => {
|
|
203
|
+
try {
|
|
204
|
+
const config = await loadConfig();
|
|
205
|
+
const client = new NervePayClient(config);
|
|
206
|
+
const result = await identity.whoami(client);
|
|
207
|
+
|
|
208
|
+
console.log(chalk.cyan('\nš¤ Agent Identity\n'));
|
|
209
|
+
console.log(chalk.gray('DID:'), chalk.white(result.agent_did));
|
|
210
|
+
console.log(chalk.gray('Name:'), chalk.white(result.name));
|
|
211
|
+
console.log(chalk.gray('Description:'), chalk.white(result.description));
|
|
212
|
+
console.log(chalk.gray('Reputation:'), chalk.white(`${result.reputation_score}/100`));
|
|
213
|
+
console.log(chalk.gray('Created:'), chalk.white(new Date(result.created_at).toLocaleString()));
|
|
214
|
+
console.log();
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error(chalk.red('ā Error:'), error.message);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// ============================================================================
|
|
222
|
+
// GATEWAYS COMMAND
|
|
223
|
+
// ============================================================================
|
|
224
|
+
|
|
225
|
+
program
|
|
226
|
+
.command('gateways')
|
|
227
|
+
.description('List connected gateways')
|
|
228
|
+
.action(async () => {
|
|
229
|
+
try {
|
|
230
|
+
const config = await loadConfig();
|
|
231
|
+
const client = new NervePayClient(config);
|
|
232
|
+
const result = await gateway.listGateways(client);
|
|
233
|
+
|
|
234
|
+
console.log(chalk.cyan('\nš Connected Gateways\n'));
|
|
235
|
+
|
|
236
|
+
if (!result.gateways || result.gateways.length === 0) {
|
|
237
|
+
console.log(chalk.yellow('No gateways connected'));
|
|
238
|
+
console.log(chalk.gray('Run:'), chalk.cyan('nervepay setup'), chalk.gray('to pair your gateway'));
|
|
239
|
+
} else {
|
|
240
|
+
result.gateways.forEach((gw, i) => {
|
|
241
|
+
console.log(chalk.white(`${i + 1}. ${gw.name}`));
|
|
242
|
+
console.log(chalk.gray(' URL:'), gw.url);
|
|
243
|
+
console.log(chalk.gray(' Status:'), gw.last_health_status === 'healthy' ? chalk.green('healthy') : chalk.yellow(gw.last_health_status));
|
|
244
|
+
console.log();
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error(chalk.red('ā Error:'), error.message);
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// ============================================================================
|
|
254
|
+
// SECRETS COMMAND
|
|
255
|
+
// ============================================================================
|
|
256
|
+
|
|
257
|
+
program
|
|
258
|
+
.command('secrets')
|
|
259
|
+
.description('List secrets in vault')
|
|
260
|
+
.action(async () => {
|
|
261
|
+
try {
|
|
262
|
+
const config = await loadConfig();
|
|
263
|
+
const client = new NervePayClient(config);
|
|
264
|
+
const result = await vault.listSecrets(client);
|
|
265
|
+
|
|
266
|
+
console.log(chalk.cyan('\nš Vault Secrets\n'));
|
|
267
|
+
|
|
268
|
+
if (!result.secrets || result.secrets.length === 0) {
|
|
269
|
+
console.log(chalk.yellow('No secrets stored'));
|
|
270
|
+
console.log(chalk.gray('Add secrets in Mission Control > Agent Passports > Secrets tab'));
|
|
271
|
+
} else {
|
|
272
|
+
result.secrets.forEach((secret, i) => {
|
|
273
|
+
console.log(chalk.white(`${i + 1}. ${secret.name}`));
|
|
274
|
+
console.log(chalk.gray(' Type:'), secret.secret_type);
|
|
275
|
+
console.log(chalk.gray(' Updated:'), new Date(secret.updated_at).toLocaleString());
|
|
276
|
+
console.log();
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
} catch (error) {
|
|
280
|
+
console.error(chalk.red('ā Error:'), error.message);
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// ============================================================================
|
|
286
|
+
// HELPER FUNCTIONS
|
|
287
|
+
// ============================================================================
|
|
288
|
+
|
|
289
|
+
async function loadConfig() {
|
|
290
|
+
try {
|
|
291
|
+
const openclawConfig = JSON.parse(await readFile(OPENCLAW_CONFIG_PATH, 'utf-8'));
|
|
292
|
+
const pluginConfig = openclawConfig.plugins?.entries?.nervepay?.config || {};
|
|
293
|
+
|
|
294
|
+
if (!pluginConfig.agentDid || !pluginConfig.privateKey) {
|
|
295
|
+
console.error(chalk.red('ā NervePay not configured'));
|
|
296
|
+
console.error(chalk.yellow(' Run:'), chalk.cyan('nervepay setup'));
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
apiUrl: pluginConfig.apiUrl || 'https://api.nervepay.xyz',
|
|
302
|
+
agentDid: pluginConfig.agentDid,
|
|
303
|
+
privateKey: pluginConfig.privateKey,
|
|
304
|
+
};
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error(chalk.red('ā Failed to load config:'), error.message);
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ============================================================================
|
|
312
|
+
// RUN
|
|
313
|
+
// ============================================================================
|
|
314
|
+
|
|
315
|
+
program.parse();
|
package/openclaw.plugin.json
CHANGED
|
@@ -29,6 +29,15 @@
|
|
|
29
29
|
"type": "string",
|
|
30
30
|
"description": "OpenClaw gateway URL (for pairing)"
|
|
31
31
|
},
|
|
32
|
+
"gatewayToken": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"description": "OpenClaw gateway authentication token"
|
|
35
|
+
},
|
|
36
|
+
"autoDetectGateway": {
|
|
37
|
+
"type": "boolean",
|
|
38
|
+
"description": "Automatically detect gateway from ~/.openclaw/openclaw.json",
|
|
39
|
+
"default": true
|
|
40
|
+
},
|
|
32
41
|
"enableOrchestration": {
|
|
33
42
|
"type": "boolean",
|
|
34
43
|
"description": "Enable multi-agent orchestration features",
|
|
@@ -53,7 +62,16 @@
|
|
|
53
62
|
},
|
|
54
63
|
"gatewayUrl": {
|
|
55
64
|
"label": "Gateway URL",
|
|
56
|
-
"placeholder": "
|
|
65
|
+
"placeholder": "ws://127.0.0.1:18789"
|
|
66
|
+
},
|
|
67
|
+
"gatewayToken": {
|
|
68
|
+
"label": "Gateway Token",
|
|
69
|
+
"placeholder": "token from ~/.openclaw/openclaw.json",
|
|
70
|
+
"sensitive": true
|
|
71
|
+
},
|
|
72
|
+
"autoDetectGateway": {
|
|
73
|
+
"label": "Auto-detect Gateway",
|
|
74
|
+
"description": "Read gateway config from ~/.openclaw/openclaw.json automatically"
|
|
57
75
|
},
|
|
58
76
|
"enableOrchestration": {
|
|
59
77
|
"label": "Enable Orchestration",
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nervepay",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "NervePay plugin for OpenClaw - Self-sovereign identity, vault, and orchestration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"nervepay": "./bin/nervepay-cli.js"
|
|
10
|
+
},
|
|
8
11
|
"openclaw": {
|
|
9
12
|
"extensions": [
|
|
10
13
|
"dist/index.js"
|
|
@@ -36,7 +39,11 @@
|
|
|
36
39
|
},
|
|
37
40
|
"dependencies": {
|
|
38
41
|
"@noble/ed25519": "^2.1.0",
|
|
39
|
-
"@noble/hashes": "^1.5.0"
|
|
42
|
+
"@noble/hashes": "^1.5.0",
|
|
43
|
+
"commander": "^12.0.0",
|
|
44
|
+
"inquirer": "^9.0.0",
|
|
45
|
+
"chalk": "^5.0.0",
|
|
46
|
+
"open": "^10.0.0"
|
|
40
47
|
},
|
|
41
48
|
"peerDependencies": {
|
|
42
49
|
"openclaw": ">=1.0.0"
|
|
@@ -51,6 +58,7 @@
|
|
|
51
58
|
},
|
|
52
59
|
"files": [
|
|
53
60
|
"dist",
|
|
61
|
+
"bin",
|
|
54
62
|
"openclaw.plugin.json",
|
|
55
63
|
"README.md",
|
|
56
64
|
"LICENSE",
|