nervepay 1.3.9 → 1.4.1

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 +86 -29
  2. package/package.json +1 -1
@@ -80,6 +80,9 @@ function findOpenClawConfigPath() {
80
80
  join(homedir(), '.config', 'openclaw', 'openclaw.json'),
81
81
  join(homedir(), '.moltbot', 'openclaw.json'),
82
82
  join(homedir(), '.clawdbot', 'openclaw.json'),
83
+ // Common system-level locations (e.g. running as root on a server)
84
+ '/home/openclaw/.openclaw/openclaw.json',
85
+ '/opt/openclaw/openclaw.json',
83
86
  ];
84
87
  for (const p of candidates) {
85
88
  if (existsSync(p)) return p;
@@ -107,7 +110,9 @@ function extractGatewayConfig(config) {
107
110
  token = config.gateway.auth.token;
108
111
  }
109
112
 
113
+ // Environment variable overrides
110
114
  if (process.env.OPENCLAW_GATEWAY_TOKEN) token = token || process.env.OPENCLAW_GATEWAY_TOKEN;
115
+ if (process.env.OPENCLAW_GATEWAY_URL) url = url || process.env.OPENCLAW_GATEWAY_URL;
111
116
  if (process.env.OPENCLAW_GATEWAY_PORT && !url) url = `http://127.0.0.1:${process.env.OPENCLAW_GATEWAY_PORT}`;
112
117
 
113
118
  return { url, token };
@@ -171,6 +176,38 @@ async function updateOpenClawConfig(agentDid, privateKey, apiUrl) {
171
176
  } catch { return false; }
172
177
  }
173
178
 
179
+ /**
180
+ * Enable /v1/chat/completions on the OpenClaw gateway.
181
+ *
182
+ * Sets gateway.http.endpoints.chatCompletions.enabled = true in openclaw.json.
183
+ * This allows NervePay Mission Control to chat directly without spawning sub-agents.
184
+ *
185
+ * Returns true if config was updated, false otherwise.
186
+ * The gateway must be restarted for the change to take effect.
187
+ */
188
+ async function enableChatCompletions() {
189
+ const configPath = findOpenClawConfigPath();
190
+ if (!configPath) return false;
191
+ try {
192
+ const config = JSON.parse(await readFile(configPath, 'utf-8'));
193
+
194
+ // Check if already enabled
195
+ if (config.gateway?.http?.endpoints?.chatCompletions?.enabled === true) {
196
+ return 'already_enabled';
197
+ }
198
+
199
+ // Deep-merge the setting
200
+ if (!config.gateway) config.gateway = {};
201
+ if (!config.gateway.http) config.gateway.http = {};
202
+ if (!config.gateway.http.endpoints) config.gateway.http.endpoints = {};
203
+ if (!config.gateway.http.endpoints.chatCompletions) config.gateway.http.endpoints.chatCompletions = {};
204
+ config.gateway.http.endpoints.chatCompletions.enabled = true;
205
+
206
+ await writeFile(configPath, JSON.stringify(config, null, 2));
207
+ return true;
208
+ } catch { return false; }
209
+ }
210
+
174
211
  // ---------------------------------------------------------------------------
175
212
  // CLI program
176
213
  // ---------------------------------------------------------------------------
@@ -351,6 +388,13 @@ program
351
388
  log(dim('Approve in Mission Control > Task Board > Incoming'));
352
389
  }
353
390
 
391
+ // Enable /v1/chat/completions for direct chat (no sub-agent spawning)
392
+ const chatResult = await enableChatCompletions();
393
+ if (chatResult === true) {
394
+ logOk('Enabled chat completions endpoint');
395
+ log(dim(' Restart gateway for this to take effect:'), info('openclaw gateway restart'));
396
+ }
397
+
354
398
  divider();
355
399
  logOk(bold('Setup complete'));
356
400
  console.log();
@@ -518,10 +562,8 @@ async function deviceNodePairing(options) {
518
562
  const timeoutMs = parseInt(options.timeout, 10) * 1000;
519
563
 
520
564
  // First attempt
521
- let deviceToken = '';
522
565
  try {
523
- const result = await attemptConnect(WebSocket, wsUrl, ctx);
524
- deviceToken = result.token;
566
+ await attemptConnect(WebSocket, wsUrl, ctx);
525
567
  logOk('Connected (device already paired)');
526
568
  } catch (e) {
527
569
  if (e.message !== 'NOT_PAIRED') throw e;
@@ -543,8 +585,7 @@ async function deviceNodePairing(options) {
543
585
  process.stdout.write(dim('.'));
544
586
 
545
587
  try {
546
- const result = await attemptConnect(WebSocket, wsUrl, ctx);
547
- deviceToken = result.token;
588
+ await attemptConnect(WebSocket, wsUrl, ctx);
548
589
  approved = true;
549
590
  console.log();
550
591
  break;
@@ -561,33 +602,38 @@ async function deviceNodePairing(options) {
561
602
  logOk(bold('Paired!'));
562
603
  console.log();
563
604
 
564
- if (deviceToken) {
565
- logInfo('Saving to NervePay...');
566
- const client = new NervePayClient({
567
- apiUrl: options.apiUrl || apiUrl,
568
- agentDid,
569
- privateKey,
570
- });
605
+ // Store the master gateway token (used for REST API calls by the backend).
606
+ // The device token from hello-ok is WS-only; the backend uses HTTP Bearer auth.
607
+ logInfo('Saving to NervePay...');
608
+ const client = new NervePayClient({
609
+ apiUrl: options.apiUrl || apiUrl,
610
+ agentDid,
611
+ privateKey,
612
+ });
571
613
 
572
- const apiResult = await gateway.completeDevicePairing(client, {
573
- device_token: deviceToken,
574
- device_id: deviceId,
575
- gateway_url: gatewayUrl,
576
- gateway_name: options.name,
577
- });
614
+ const apiResult = await gateway.completeDevicePairing(client, {
615
+ device_token: gatewayToken,
616
+ device_id: deviceId,
617
+ gateway_url: gatewayUrl,
618
+ gateway_name: options.name,
619
+ });
578
620
 
579
- divider();
580
- logOk(bold('Gateway paired'));
581
- field(' Gateway ID', apiResult.gateway_id);
582
- field(' Device ID', deviceId.slice(0, 16) + '...');
583
- field(' Agent DID', agentDid);
584
- } else {
585
- divider();
586
- logOk(bold('Device approved by gateway'));
587
- field(' Device ID', deviceId.slice(0, 16) + '...');
588
- field(' Agent DID', agentDid);
589
- log(warn(' No device token received — gateway may not issue tokens for this role'));
621
+ divider();
622
+ logOk(bold('Gateway paired'));
623
+ field(' Gateway ID', apiResult.gateway_id);
624
+ field(' Device ID', deviceId.slice(0, 16) + '...');
625
+ field(' Agent DID', agentDid);
626
+ console.log();
627
+
628
+ // Enable /v1/chat/completions for direct chat (no sub-agent spawning)
629
+ const chatResult = await enableChatCompletions();
630
+ if (chatResult === true) {
631
+ logOk('Enabled chat completions endpoint');
632
+ log(dim(' Restart gateway for this to take effect:'), info('openclaw gateway restart'));
633
+ } else if (chatResult === 'already_enabled') {
634
+ logOk('Chat completions endpoint already enabled');
590
635
  }
636
+
591
637
  console.log();
592
638
  log(dim('View in dashboard:'), info('Mission Control > Task Board'));
593
639
  console.log();
@@ -708,6 +754,17 @@ async function legacyCodePairing(options) {
708
754
  logOk(bold('Gateway paired'));
709
755
  field(' Gateway ID', result.gateway_id || 'created');
710
756
  field(' Agent DID', agentDid);
757
+ console.log();
758
+
759
+ // Enable /v1/chat/completions for direct chat (no sub-agent spawning)
760
+ const chatResult = await enableChatCompletions();
761
+ if (chatResult === true) {
762
+ logOk('Enabled chat completions endpoint');
763
+ log(dim(' Restart gateway for this to take effect:'), info('openclaw gateway restart'));
764
+ } else if (chatResult === 'already_enabled') {
765
+ logOk('Chat completions endpoint already enabled');
766
+ }
767
+
711
768
  console.log();
712
769
  log(dim('View in dashboard:'), info('Mission Control > Task Board'));
713
770
  console.log();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nervepay",
3
- "version": "1.3.9",
3
+ "version": "1.4.1",
4
4
  "description": "NervePay plugin for OpenClaw - Self-sovereign identity, vault, and orchestration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",