nervepay 1.6.6 → 1.6.9
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/CHANGELOG.md +1 -1
- package/bin/nervepay-cli.js +139 -14
- package/dist/tools/identity.d.ts +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -105,7 +105,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
105
105
|
- [ ] Session tokens (reduce signature overhead)
|
|
106
106
|
- [ ] ERC-4337 smart wallet integration
|
|
107
107
|
- [ ] ERC-8004 on-chain reputation bridge
|
|
108
|
-
- [ ]
|
|
108
|
+
- [ ] Agent attestation network
|
|
109
109
|
- [ ] Sub-agent delegation tools
|
|
110
110
|
- [ ] Real-time analytics dashboard
|
|
111
111
|
- [ ] Multi-gateway load balancing
|
package/bin/nervepay-cli.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
8
|
-
import { existsSync } from 'fs';
|
|
8
|
+
import { existsSync, readFileSync } from 'fs';
|
|
9
9
|
import { execSync } from 'child_process';
|
|
10
10
|
import { homedir, networkInterfaces } from 'os';
|
|
11
11
|
import { join, dirname } from 'path';
|
|
@@ -80,6 +80,93 @@ function expandHomePath(inputPath) {
|
|
|
80
80
|
return trimmed;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
let openClawEnvCache = null;
|
|
84
|
+
|
|
85
|
+
function parseEnvFile(content) {
|
|
86
|
+
const map = {};
|
|
87
|
+
for (const line of String(content || '').split(/\r?\n/)) {
|
|
88
|
+
const trimmed = line.trim();
|
|
89
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
90
|
+
const normalized = trimmed.startsWith('export ') ? trimmed.slice(7).trim() : trimmed;
|
|
91
|
+
const eq = normalized.indexOf('=');
|
|
92
|
+
if (eq <= 0) continue;
|
|
93
|
+
const key = normalized.slice(0, eq).trim();
|
|
94
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue;
|
|
95
|
+
let value = normalized.slice(eq + 1).trim();
|
|
96
|
+
if (
|
|
97
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
98
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
99
|
+
) {
|
|
100
|
+
value = value.slice(1, -1);
|
|
101
|
+
}
|
|
102
|
+
map[key] = value;
|
|
103
|
+
}
|
|
104
|
+
return map;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function loadOpenClawEnvFile() {
|
|
108
|
+
if (openClawEnvCache !== null) return openClawEnvCache;
|
|
109
|
+
const candidates = [
|
|
110
|
+
process.env.OPENCLAW_ENV_PATH,
|
|
111
|
+
'/opt/openclaw.env',
|
|
112
|
+
'/etc/openclaw.env',
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
for (const candidate of candidates) {
|
|
116
|
+
const filePath = expandHomePath(candidate);
|
|
117
|
+
if (!filePath || !existsSync(filePath)) continue;
|
|
118
|
+
try {
|
|
119
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
120
|
+
openClawEnvCache = parseEnvFile(content);
|
|
121
|
+
return openClawEnvCache;
|
|
122
|
+
} catch {
|
|
123
|
+
// Try next candidate.
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
openClawEnvCache = {};
|
|
128
|
+
return openClawEnvCache;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function parseGatewayUrlInput(raw) {
|
|
132
|
+
if (typeof raw !== 'string') return { url: null, token: null };
|
|
133
|
+
const value = raw.trim();
|
|
134
|
+
if (!value) return { url: null, token: null };
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const parsed = new URL(value.includes('://') ? value : `http://${value}`);
|
|
138
|
+
const token =
|
|
139
|
+
parsed.searchParams.get('token') ||
|
|
140
|
+
parsed.searchParams.get('gateway_token') ||
|
|
141
|
+
parsed.searchParams.get('gatewayToken') ||
|
|
142
|
+
parsed.searchParams.get('access_token');
|
|
143
|
+
|
|
144
|
+
if (token) {
|
|
145
|
+
parsed.searchParams.delete('token');
|
|
146
|
+
parsed.searchParams.delete('gateway_token');
|
|
147
|
+
parsed.searchParams.delete('gatewayToken');
|
|
148
|
+
parsed.searchParams.delete('access_token');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const cleanUrl = parsed.toString().replace(/\?$/, '');
|
|
152
|
+
return {
|
|
153
|
+
url: cleanUrl || value,
|
|
154
|
+
token: token && token.trim() ? token.trim() : null,
|
|
155
|
+
};
|
|
156
|
+
} catch {
|
|
157
|
+
return { url: value, token: null };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function normalizeGatewayInput(url, token) {
|
|
162
|
+
const parsed = parseGatewayUrlInput(url);
|
|
163
|
+
return {
|
|
164
|
+
url: parsed.url || url || null,
|
|
165
|
+
token: token || parsed.token || null,
|
|
166
|
+
tokenFromUrl: Boolean(parsed.token && !token),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
83
170
|
function findOpenClawConfigPath() {
|
|
84
171
|
const candidates = [];
|
|
85
172
|
const seen = new Set();
|
|
@@ -349,6 +436,7 @@ function extractGatewayConfig(config) {
|
|
|
349
436
|
let publicUrl = null;
|
|
350
437
|
let token = null;
|
|
351
438
|
let warning = null;
|
|
439
|
+
const openClawEnv = loadOpenClawEnvFile();
|
|
352
440
|
|
|
353
441
|
const mode = config?.gateway?.mode;
|
|
354
442
|
const port = Number(config?.gateway?.port || config?.port || process.env.OPENCLAW_GATEWAY_PORT || 18789);
|
|
@@ -424,6 +512,16 @@ function extractGatewayConfig(config) {
|
|
|
424
512
|
process.env.NERVEPAY_GATEWAY_URL ||
|
|
425
513
|
process.env.OPENCLAW_PUBLIC_URL ||
|
|
426
514
|
process.env.NERVEPAY_PUBLIC_URL ||
|
|
515
|
+
openClawEnv.OPENCLAW_GATEWAY_URL ||
|
|
516
|
+
openClawEnv.NERVEPAY_GATEWAY_URL ||
|
|
517
|
+
openClawEnv.OPENCLAW_PUBLIC_URL ||
|
|
518
|
+
openClawEnv.NERVEPAY_PUBLIC_URL ||
|
|
519
|
+
openClawEnv.OPENCLAW_DASHBOARD_URL ||
|
|
520
|
+
openClawEnv.DASHBOARD_URL ||
|
|
521
|
+
openClawEnv.APP_URL ||
|
|
522
|
+
openClawEnv.OPENCLAW_URL ||
|
|
523
|
+
openClawEnv.OPENCLAW_EXTERNAL_URL ||
|
|
524
|
+
openClawEnv.PUBLIC_URL ||
|
|
427
525
|
config?.gateway?.publicUrl ||
|
|
428
526
|
config?.gateway?.public_url ||
|
|
429
527
|
config?.gateway?.url ||
|
|
@@ -432,7 +530,13 @@ function extractGatewayConfig(config) {
|
|
|
432
530
|
config?.url;
|
|
433
531
|
|
|
434
532
|
if (typeof explicitPublic === 'string' && explicitPublic.trim()) {
|
|
435
|
-
|
|
533
|
+
const parsedPublic = parseGatewayUrlInput(explicitPublic.trim());
|
|
534
|
+
if (parsedPublic.url) {
|
|
535
|
+
publicUrl = ensureHttpScheme(parsedPublic.url, secureGateway);
|
|
536
|
+
}
|
|
537
|
+
if (!token && parsedPublic.token) {
|
|
538
|
+
token = parsedPublic.token;
|
|
539
|
+
}
|
|
436
540
|
} else if (!publicUrl) {
|
|
437
541
|
if (!isLocalHost(configuredHost) && configuredHost !== '0.0.0.0' && configuredHost !== '::') {
|
|
438
542
|
publicUrl = buildGatewayUrl({ scheme, host: configuredHost, port });
|
|
@@ -457,6 +561,14 @@ function extractGatewayConfig(config) {
|
|
|
457
561
|
}
|
|
458
562
|
|
|
459
563
|
// Environment token overrides should win.
|
|
564
|
+
if (!token) {
|
|
565
|
+
token =
|
|
566
|
+
openClawEnv.OPENCLAW_GATEWAY_TOKEN ||
|
|
567
|
+
openClawEnv.OPENCLAW_GATEWAY_PASSWORD ||
|
|
568
|
+
openClawEnv.OPENCLAW_TOKEN ||
|
|
569
|
+
openClawEnv.GATEWAY_TOKEN ||
|
|
570
|
+
token;
|
|
571
|
+
}
|
|
460
572
|
if (process.env.OPENCLAW_GATEWAY_TOKEN) token = process.env.OPENCLAW_GATEWAY_TOKEN;
|
|
461
573
|
if (!token && process.env.OPENCLAW_GATEWAY_PASSWORD) token = process.env.OPENCLAW_GATEWAY_PASSWORD;
|
|
462
574
|
|
|
@@ -732,7 +844,7 @@ program
|
|
|
732
844
|
.command('setup')
|
|
733
845
|
.description('Create agent identity and pair gateway')
|
|
734
846
|
.option('--api-url <url>', 'NervePay API URL', DEFAULT_API_URL)
|
|
735
|
-
.option('--gateway-url <url>', 'Gateway URL override')
|
|
847
|
+
.option('--gateway-url <url>', 'Gateway URL override (OpenClaw Dashboard URL also works)')
|
|
736
848
|
.option('--gateway-token <token>', 'Gateway token override')
|
|
737
849
|
.action(async (options) => {
|
|
738
850
|
banner();
|
|
@@ -743,9 +855,13 @@ program
|
|
|
743
855
|
const openclawConfig = await readOpenClawConfig();
|
|
744
856
|
const configPath = findOpenClawConfigPath();
|
|
745
857
|
const gw = extractGatewayConfig(openclawConfig);
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
858
|
+
const normalizedSetupInput = normalizeGatewayInput(options.gatewayUrl, options.gatewayToken);
|
|
859
|
+
if (normalizedSetupInput.tokenFromUrl) {
|
|
860
|
+
logInfo('Extracted gateway token from --gateway-url query parameter');
|
|
861
|
+
}
|
|
862
|
+
let gatewayConnectUrl = normalizedSetupInput.url || gw.connectUrl || gw.url;
|
|
863
|
+
let gatewayPublicUrl = normalizedSetupInput.url || gw.publicUrl || gw.url || gatewayConnectUrl;
|
|
864
|
+
let gatewayToken = normalizedSetupInput.token || gw.token;
|
|
749
865
|
|
|
750
866
|
if (gatewayConnectUrl) {
|
|
751
867
|
logOk(`Found OpenClaw gateway: ${gatewayConnectUrl}`);
|
|
@@ -863,6 +979,7 @@ program
|
|
|
863
979
|
if (!isExternallyReachableUrlCandidate(gatewayPublicUrl || gatewayConnectUrl)) {
|
|
864
980
|
logWarn('Gateway pairing skipped: public URL is local-only or missing.');
|
|
865
981
|
log(dim('Set a reachable URL via OPENCLAW_GATEWAY_URL or --gateway-url, and ensure gateway.bind is "lan" or behind a public reverse proxy.'));
|
|
982
|
+
log(dim('Tip: you can paste your OpenClaw Dashboard URL directly (token is auto-extracted).'));
|
|
866
983
|
log(dim('Pair later with:'), info('nervepay pair --gateway-url <public-url>'));
|
|
867
984
|
} else {
|
|
868
985
|
console.log();
|
|
@@ -934,7 +1051,7 @@ program
|
|
|
934
1051
|
.description('Connect gateway via device protocol or pairing code')
|
|
935
1052
|
.option('--code <code>', 'Pairing code from dashboard (legacy flow)')
|
|
936
1053
|
.option('--api-url <url>', 'NervePay API URL', DEFAULT_API_URL)
|
|
937
|
-
.option('--gateway-url <url>', 'Gateway URL')
|
|
1054
|
+
.option('--gateway-url <url>', 'Gateway URL (or Dashboard URL with ?token=...)')
|
|
938
1055
|
.option('--gateway-token <token>', 'Gateway auth token')
|
|
939
1056
|
.option('--name <name>', 'Gateway display name', 'OpenClaw Gateway')
|
|
940
1057
|
.option('--timeout <seconds>', 'Approval timeout in seconds', '300')
|
|
@@ -1174,9 +1291,13 @@ async function deviceNodePairing(options) {
|
|
|
1174
1291
|
field(' DID', agentDid);
|
|
1175
1292
|
|
|
1176
1293
|
// Resolve gateway URLs and token
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1294
|
+
const normalizedInput = normalizeGatewayInput(options.gatewayUrl, options.gatewayToken);
|
|
1295
|
+
if (normalizedInput.tokenFromUrl) {
|
|
1296
|
+
logInfo('Extracted gateway token from --gateway-url query parameter');
|
|
1297
|
+
}
|
|
1298
|
+
let gatewayConnectUrl = normalizedInput.url;
|
|
1299
|
+
let gatewayPublicUrl = normalizedInput.url;
|
|
1300
|
+
let gatewayToken = normalizedInput.token;
|
|
1180
1301
|
if (!gatewayConnectUrl || !gatewayToken || !gatewayPublicUrl) {
|
|
1181
1302
|
const oc = await readOpenClawConfig();
|
|
1182
1303
|
const gwConf = extractGatewayConfig(oc);
|
|
@@ -1192,7 +1313,7 @@ async function deviceNodePairing(options) {
|
|
|
1192
1313
|
if (!isExternallyReachableUrlCandidate(gatewayPublicUrl || gatewayConnectUrl)) {
|
|
1193
1314
|
die(
|
|
1194
1315
|
'Gateway public URL is local-only. NervePay cloud cannot reach localhost/127.0.0.1.',
|
|
1195
|
-
'Set OPENCLAW_GATEWAY_URL or pass --gateway-url <public-url> and ensure gateway.bind is "lan" (or use a public reverse proxy).'
|
|
1316
|
+
'Set OPENCLAW_GATEWAY_URL or pass --gateway-url <public-url> and ensure gateway.bind is "lan" (or use a public reverse proxy). You can paste your OpenClaw Dashboard URL directly; token query params are auto-extracted.'
|
|
1196
1317
|
);
|
|
1197
1318
|
}
|
|
1198
1319
|
|
|
@@ -1321,8 +1442,12 @@ async function legacyCodePairing(options) {
|
|
|
1321
1442
|
console.log();
|
|
1322
1443
|
logInfo('Reading gateway config...');
|
|
1323
1444
|
|
|
1324
|
-
|
|
1325
|
-
|
|
1445
|
+
const normalizedInput = normalizeGatewayInput(options.gatewayUrl, options.gatewayToken);
|
|
1446
|
+
if (normalizedInput.tokenFromUrl) {
|
|
1447
|
+
logInfo('Extracted gateway token from --gateway-url query parameter');
|
|
1448
|
+
}
|
|
1449
|
+
let gatewayUrl = normalizedInput.url;
|
|
1450
|
+
let gatewayToken = normalizedInput.token;
|
|
1326
1451
|
|
|
1327
1452
|
if (!gatewayUrl || !gatewayToken) {
|
|
1328
1453
|
const oc = await readOpenClawConfig();
|
|
@@ -1339,7 +1464,7 @@ async function legacyCodePairing(options) {
|
|
|
1339
1464
|
if (!isExternallyReachableUrlCandidate(gatewayUrl)) {
|
|
1340
1465
|
die(
|
|
1341
1466
|
'Gateway URL is local-only. NervePay cloud cannot reach localhost/127.0.0.1.',
|
|
1342
|
-
'Use --gateway-url <public-url> and ensure gateway.bind is "lan" (or use a public reverse proxy).'
|
|
1467
|
+
'Use --gateway-url <public-url> and ensure gateway.bind is "lan" (or use a public reverse proxy). You can paste your OpenClaw Dashboard URL directly; token query params are auto-extracted.'
|
|
1343
1468
|
);
|
|
1344
1469
|
}
|
|
1345
1470
|
|
package/dist/tools/identity.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nervepay",
|
|
3
|
-
"version": "1.6.
|
|
4
|
-
"description": "NervePay plugin for OpenClaw -
|
|
3
|
+
"version": "1.6.9",
|
|
4
|
+
"description": "NervePay plugin for OpenClaw - Agent identity, vault, and orchestration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|