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 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
- - [ ] x402 payment protocol support
108
+ - [ ] Agent attestation network
109
109
  - [ ] Sub-agent delegation tools
110
110
  - [ ] Real-time analytics dashboard
111
111
  - [ ] Multi-gateway load balancing
@@ -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
- publicUrl = ensureHttpScheme(explicitPublic.trim(), secureGateway);
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
- let gatewayConnectUrl = options.gatewayUrl || gw.connectUrl || gw.url;
747
- let gatewayPublicUrl = options.gatewayUrl || gw.publicUrl || gw.url || gatewayConnectUrl;
748
- let gatewayToken = options.gatewayToken || gw.token;
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
- let gatewayConnectUrl = options.gatewayUrl;
1178
- let gatewayPublicUrl = options.gatewayUrl;
1179
- let gatewayToken = options.gatewayToken;
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
- let gatewayUrl = options.gatewayUrl;
1325
- let gatewayToken = options.gatewayToken;
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
 
@@ -26,7 +26,7 @@ export interface WhoAmIResult {
26
26
  name: string;
27
27
  public_key: string;
28
28
  capabilities: {
29
- max_spending_limit_usd?: number;
29
+ max_operations_per_day?: number;
30
30
  allowed_operations?: string[];
31
31
  };
32
32
  reputation_score: number;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nervepay",
3
- "version": "1.6.6",
4
- "description": "NervePay plugin for OpenClaw - Self-sovereign identity, vault, and orchestration",
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",