nervepay 1.3.5 → 1.3.6
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 +34 -9
- package/package.json +1 -1
package/bin/nervepay-cli.js
CHANGED
|
@@ -437,7 +437,9 @@ async function deviceNodePairing(options) {
|
|
|
437
437
|
|
|
438
438
|
// Pre-compute crypto outside WS handler to avoid async race conditions
|
|
439
439
|
const pubKeyBytes = decodeBase58(publicKeyBase58);
|
|
440
|
-
|
|
440
|
+
// OpenClaw expects base64url encoding (no padding) for public key
|
|
441
|
+
const pubKeyB64Url = Buffer.from(pubKeyBytes).toString('base64')
|
|
442
|
+
.replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '');
|
|
441
443
|
|
|
442
444
|
const result = await new Promise((resolve, reject) => {
|
|
443
445
|
let settled = false;
|
|
@@ -472,13 +474,36 @@ async function deviceNodePairing(options) {
|
|
|
472
474
|
const frame = JSON.parse(data.toString());
|
|
473
475
|
logDebug('<<', JSON.stringify(frame).slice(0, 300));
|
|
474
476
|
|
|
475
|
-
// Challenge → sign and connect
|
|
477
|
+
// Challenge → build auth payload, sign, and connect
|
|
476
478
|
if (frame.event === 'connect.challenge') {
|
|
477
479
|
const nonce = frame.payload?.nonce || '';
|
|
478
480
|
logOk('Challenge received');
|
|
479
481
|
|
|
480
|
-
const
|
|
481
|
-
const
|
|
482
|
+
const signedAtMs = Date.now();
|
|
483
|
+
const role = 'operator';
|
|
484
|
+
const scopes = ['operator.read', 'operator.write'];
|
|
485
|
+
|
|
486
|
+
// Build structured payload per OpenClaw device-auth protocol (v2)
|
|
487
|
+
// Format: v2|deviceId|clientId|clientMode|role|scopes|signedAtMs|token|nonce
|
|
488
|
+
const payload = [
|
|
489
|
+
'v2',
|
|
490
|
+
deviceId,
|
|
491
|
+
'node-host',
|
|
492
|
+
'node',
|
|
493
|
+
role,
|
|
494
|
+
scopes.join(','),
|
|
495
|
+
String(signedAtMs),
|
|
496
|
+
'', // token (empty for initial connect)
|
|
497
|
+
nonce,
|
|
498
|
+
].join('|');
|
|
499
|
+
|
|
500
|
+
logDebug('Auth payload:', payload);
|
|
501
|
+
|
|
502
|
+
const payloadBytes = new TextEncoder().encode(payload);
|
|
503
|
+
const signatureB64 = await signRawBytes(privateKey, payloadBytes);
|
|
504
|
+
// Convert to base64url (no padding)
|
|
505
|
+
const signatureB64Url = signatureB64
|
|
506
|
+
.replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '');
|
|
482
507
|
|
|
483
508
|
connectId = crypto.randomUUID();
|
|
484
509
|
ws.send(JSON.stringify({
|
|
@@ -486,10 +511,10 @@ async function deviceNodePairing(options) {
|
|
|
486
511
|
id: connectId,
|
|
487
512
|
method: 'connect',
|
|
488
513
|
params: {
|
|
489
|
-
role
|
|
514
|
+
role,
|
|
490
515
|
minProtocol: 3,
|
|
491
516
|
maxProtocol: 3,
|
|
492
|
-
scopes
|
|
517
|
+
scopes,
|
|
493
518
|
client: {
|
|
494
519
|
id: 'node-host',
|
|
495
520
|
version: VERSION,
|
|
@@ -498,9 +523,9 @@ async function deviceNodePairing(options) {
|
|
|
498
523
|
},
|
|
499
524
|
device: {
|
|
500
525
|
id: deviceId,
|
|
501
|
-
publicKey:
|
|
502
|
-
signature:
|
|
503
|
-
signedAt:
|
|
526
|
+
publicKey: pubKeyB64Url,
|
|
527
|
+
signature: signatureB64Url,
|
|
528
|
+
signedAt: signedAtMs,
|
|
504
529
|
nonce,
|
|
505
530
|
},
|
|
506
531
|
},
|