robot-resources 1.7.2 → 1.7.4

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.
@@ -6,42 +6,6 @@ import { join, dirname } from 'node:path';
6
6
  import { isOpenClawInstalled, isOpenClawPluginInstalled, getOpenClawAuthMode } from './detect.js';
7
7
  import { stripJson5 } from './json5.js';
8
8
 
9
- /**
10
- * Heartbeat interval in ms. OC kills processes after 5s of no output
11
- * (noOutputTimeoutMs = 5000). This must stay under that threshold.
12
- */
13
- const HEARTBEAT_INTERVAL_MS = 4000;
14
-
15
- /**
16
- * Run a command with a heartbeat to keep agent sessions alive.
17
- */
18
- function spawnWithHeartbeat(cmd, args, { label, timeout = 30_000 } = {}) {
19
- return new Promise((resolve, reject) => {
20
- const proc = spawn(cmd, args, {
21
- stdio: ['ignore', 'pipe', 'pipe'],
22
- timeout,
23
- });
24
-
25
- process.stdout.write(` ${label}...\n`);
26
- let seconds = 0;
27
- const heartbeat = setInterval(() => {
28
- seconds += HEARTBEAT_INTERVAL_MS / 1000;
29
- process.stdout.write(` ${label}... ${seconds}s\n`);
30
- }, HEARTBEAT_INTERVAL_MS);
31
-
32
- proc.on('close', (code) => {
33
- clearInterval(heartbeat);
34
- if (code === 0) resolve();
35
- else reject(new Error(`${cmd} ${args.join(' ')} exited with code ${code}`));
36
- });
37
-
38
- proc.on('error', (err) => {
39
- clearInterval(heartbeat);
40
- reject(err);
41
- });
42
- });
43
- }
44
-
45
9
  /**
46
10
  * Read openclaw.json, creating it with a minimal structure if it doesn't exist.
47
11
  * Returns parsed config object. Throws on malformed JSON (caller handles).
@@ -259,30 +223,20 @@ export function configureToolRouting() {
259
223
 
260
224
  /**
261
225
  * Restart the OpenClaw gateway so it picks up new plugin + config.
262
- * Retries up to 3 times with 2s backoffgateway restart MUST succeed
263
- * for MCP tools to be available. Silent failure = tools broken for user.
226
+ * Spawns a detached process that waits 5s then restarts lets the wizard
227
+ * finish and return output to OC before the gateway dies.
228
+ *
229
+ * If called from inside an OC agent session (exec: npx robot-resources),
230
+ * a synchronous restart kills the session before output is captured.
231
+ * The deferred approach avoids this: wizard exits → OC captures output →
232
+ * 5s later gateway restarts → next conversation has all tools loaded.
264
233
  */
265
234
  async function restartOpenClawGateway() {
266
- const MAX_RETRIES = 3;
267
- const BACKOFF_MS = 2000;
268
- let lastError;
269
-
270
- for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
271
- try {
272
- await spawnWithHeartbeat('openclaw', ['gateway', 'restart'], {
273
- label: `Restarting gateway${attempt > 1 ? ` (attempt ${attempt}/${MAX_RETRIES})` : ''}`,
274
- timeout: 15_000,
275
- });
276
- return; // success
277
- } catch (err) {
278
- lastError = err;
279
- if (attempt < MAX_RETRIES) {
280
- await new Promise(r => setTimeout(r, BACKOFF_MS));
281
- }
282
- }
283
- }
284
-
285
- throw lastError;
235
+ spawn('sh', ['-c', 'sleep 5 && openclaw gateway restart'], {
236
+ stdio: 'ignore',
237
+ detached: true,
238
+ }).unref();
239
+ process.stdout.write(' Restarting gateway...\n');
286
240
  }
287
241
 
288
242
  // Exported for testing and direct use
package/lib/wizard.js CHANGED
@@ -339,23 +339,6 @@ export async function runWizard({ nonInteractive = false } = {}) {
339
339
  blank();
340
340
  }
341
341
 
342
- // ── Best-effort: Single gateway restart ────────────────────────────────
343
- //
344
- // Merged from two previous restarts (after plugin install + after scraper
345
- // registration). Everything is already installed and registered above.
346
- // If the session dies during this restart, the gateway picks up changes
347
- // on its next natural restart.
348
-
349
- if (isOpenClawInstalled() && (results.tools?.some(r => r.action === 'installed') || scraperRegistered)) {
350
- try {
351
- await restartOpenClawGateway();
352
- success('OpenClaw gateway restarted');
353
- } catch {
354
- warn('Gateway restart failed — tools may not be available until OpenClaw restarts.');
355
- warn('Run manually: openclaw gateway restart');
356
- }
357
- }
358
-
359
342
  // ── Post-install health check ──────────────────────────────────────────
360
343
 
361
344
  let healthReport = null;
@@ -398,4 +381,22 @@ export async function runWizard({ nonInteractive = false } = {}) {
398
381
  // Non-fatal — status file is a convenience, not required
399
382
  }
400
383
  }
384
+
385
+ // ── Best-effort: Deferred gateway restart (MUST be last) ───────────────
386
+ //
387
+ // Gateway restart loads the newly installed plugin + scraper MCP.
388
+ // Without it, tools aren't available until the next natural gateway restart.
389
+ //
390
+ // MUST be the last thing in the wizard. The deferred spawn lets the wizard
391
+ // exit and return output to OC before the gateway dies. Silent catch —
392
+ // the plugin's install message already tells the user to start a new
393
+ // conversation. No warn/success needed.
394
+ //
395
+ // History: PR #89 (Manuel) had this right — restart last, silent catch.
396
+ // Session #25 moved it earlier with 3x retries, which killed OC Telegram
397
+ // sessions. Reverted to deferred spawn, positioned last.
398
+
399
+ if (isOpenClawInstalled() && (results.tools?.some(r => r.action === 'installed') || scraperRegistered)) {
400
+ try { await restartOpenClawGateway(); } catch { /* silent — best effort */ }
401
+ }
401
402
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "robot-resources",
3
- "version": "1.7.2",
3
+ "version": "1.7.4",
4
4
  "description": "Robot Resources — AI agent tools. One command to install everything.",
5
5
  "type": "module",
6
6
  "bin": {