nervepay 1.3.5 → 1.3.7

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.
Files changed (2) hide show
  1. package/bin/nervepay-cli.js +45 -13
  2. package/package.json +1 -1
@@ -380,7 +380,7 @@ program
380
380
  .option('--code <code>', 'Pairing code from dashboard (legacy flow)')
381
381
  .option('--api-url <url>', 'NervePay API URL', DEFAULT_API_URL)
382
382
  .option('--gateway-url <url>', 'Gateway URL')
383
- .option('--gateway-token <token>', 'Gateway token (only for --code flow)')
383
+ .option('--gateway-token <token>', 'Gateway auth token')
384
384
  .option('--name <name>', 'Gateway display name', 'OpenClaw Gateway')
385
385
  .option('--timeout <seconds>', 'Approval timeout in seconds', '300')
386
386
  .action(async (options) => {
@@ -412,13 +412,17 @@ async function deviceNodePairing(options) {
412
412
  logOk('Agent loaded');
413
413
  field(' DID', agentDid);
414
414
 
415
- // Resolve gateway URL
415
+ // Resolve gateway URL and token
416
416
  let gatewayUrl = options.gatewayUrl;
417
- if (!gatewayUrl) {
417
+ let gatewayToken = options.gatewayToken;
418
+ if (!gatewayUrl || !gatewayToken) {
418
419
  const oc = await readOpenClawConfig();
419
- gatewayUrl = extractGatewayConfig(oc).url;
420
+ const gwConf = extractGatewayConfig(oc);
421
+ gatewayUrl = gatewayUrl || gwConf.url;
422
+ gatewayToken = gatewayToken || gwConf.token;
420
423
  }
421
424
  if (!gatewayUrl) die('No gateway URL found', 'Use: nervepay pair --gateway-url <url>');
425
+ if (!gatewayToken) die('No gateway token found', 'Use: nervepay pair --gateway-token <token>');
422
426
 
423
427
  const wsUrl = httpToWs(gatewayUrl);
424
428
  field(' Gateway', wsUrl);
@@ -437,7 +441,9 @@ async function deviceNodePairing(options) {
437
441
 
438
442
  // Pre-compute crypto outside WS handler to avoid async race conditions
439
443
  const pubKeyBytes = decodeBase58(publicKeyBase58);
440
- const pubKeyB64 = Buffer.from(pubKeyBytes).toString('base64');
444
+ // OpenClaw expects base64url encoding (no padding) for public key
445
+ const pubKeyB64Url = Buffer.from(pubKeyBytes).toString('base64')
446
+ .replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '');
441
447
 
442
448
  const result = await new Promise((resolve, reject) => {
443
449
  let settled = false;
@@ -472,13 +478,36 @@ async function deviceNodePairing(options) {
472
478
  const frame = JSON.parse(data.toString());
473
479
  logDebug('<<', JSON.stringify(frame).slice(0, 300));
474
480
 
475
- // Challenge → sign and connect
481
+ // Challenge → build auth payload, sign, and connect
476
482
  if (frame.event === 'connect.challenge') {
477
483
  const nonce = frame.payload?.nonce || '';
478
484
  logOk('Challenge received');
479
485
 
480
- const nonceBytes = new TextEncoder().encode(nonce);
481
- const signatureB64 = await signRawBytes(privateKey, nonceBytes);
486
+ const signedAtMs = Date.now();
487
+ const role = 'operator';
488
+ const scopes = ['operator.read', 'operator.write'];
489
+
490
+ // Build structured payload per OpenClaw device-auth protocol (v2)
491
+ // Format: v2|deviceId|clientId|clientMode|role|scopes|signedAtMs|token|nonce
492
+ const payload = [
493
+ 'v2',
494
+ deviceId,
495
+ 'node-host',
496
+ 'node',
497
+ role,
498
+ scopes.join(','),
499
+ String(signedAtMs),
500
+ gatewayToken,
501
+ nonce,
502
+ ].join('|');
503
+
504
+ logDebug('Auth payload:', payload);
505
+
506
+ const payloadBytes = new TextEncoder().encode(payload);
507
+ const signatureB64 = await signRawBytes(privateKey, payloadBytes);
508
+ // Convert to base64url (no padding)
509
+ const signatureB64Url = signatureB64
510
+ .replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '');
482
511
 
483
512
  connectId = crypto.randomUUID();
484
513
  ws.send(JSON.stringify({
@@ -486,10 +515,10 @@ async function deviceNodePairing(options) {
486
515
  id: connectId,
487
516
  method: 'connect',
488
517
  params: {
489
- role: 'operator',
518
+ role,
490
519
  minProtocol: 3,
491
520
  maxProtocol: 3,
492
- scopes: ['operator.read', 'operator.write'],
521
+ scopes,
493
522
  client: {
494
523
  id: 'node-host',
495
524
  version: VERSION,
@@ -498,11 +527,14 @@ async function deviceNodePairing(options) {
498
527
  },
499
528
  device: {
500
529
  id: deviceId,
501
- publicKey: pubKeyB64,
502
- signature: signatureB64,
503
- signedAt: Date.now(),
530
+ publicKey: pubKeyB64Url,
531
+ signature: signatureB64Url,
532
+ signedAt: signedAtMs,
504
533
  nonce,
505
534
  },
535
+ auth: {
536
+ token: gatewayToken,
537
+ },
506
538
  },
507
539
  }));
508
540
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nervepay",
3
- "version": "1.3.5",
3
+ "version": "1.3.7",
4
4
  "description": "NervePay plugin for OpenClaw - Self-sovereign identity, vault, and orchestration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",