robot-resources 1.7.6 → 1.7.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.
@@ -1,3 +1,4 @@
1
+ import { spawn } from 'node:child_process';
1
2
  import { createRequire } from 'node:module';
2
3
  import { readFileSync, writeFileSync, copyFileSync, mkdirSync, existsSync } from 'node:fs';
3
4
  import { homedir } from 'node:os';
@@ -220,5 +221,49 @@ export function configureToolRouting() {
220
221
  return results;
221
222
  }
222
223
 
224
+ /**
225
+ * Run a command with a heartbeat to keep agent sessions alive.
226
+ * OC kills processes after 5s of no output (noOutputTimeoutMs = 5000).
227
+ * Prints immediately, then every 4s (safely under the 5s threshold).
228
+ */
229
+ function spawnWithHeartbeat(cmd, args, { label, timeout = 30_000 } = {}) {
230
+ return new Promise((resolve, reject) => {
231
+ const proc = spawn(cmd, args, {
232
+ stdio: ['ignore', 'pipe', 'pipe'],
233
+ timeout,
234
+ });
235
+
236
+ process.stdout.write(` ${label}...\n`);
237
+ let seconds = 0;
238
+ const heartbeat = setInterval(() => {
239
+ seconds += 4;
240
+ process.stdout.write(` ${label}... ${seconds}s\n`);
241
+ }, 4000);
242
+
243
+ proc.on('close', (code) => {
244
+ clearInterval(heartbeat);
245
+ if (code === 0) resolve();
246
+ else reject(new Error(`${cmd} ${args.join(' ')} exited with code ${code}`));
247
+ });
248
+
249
+ proc.on('error', (err) => {
250
+ clearInterval(heartbeat);
251
+ reject(err);
252
+ });
253
+ });
254
+ }
255
+
256
+ /**
257
+ * Restart the OpenClaw gateway so it picks up new plugin + config.
258
+ * Uses heartbeat to keep OC sessions alive during the restart.
259
+ * Telegram survives this restart — tested end-to-end (PR #89).
260
+ */
261
+ async function restartOpenClawGateway() {
262
+ await spawnWithHeartbeat('openclaw', ['gateway', 'restart'], {
263
+ label: 'Restarting gateway',
264
+ timeout: 15_000,
265
+ });
266
+ }
267
+
223
268
  // Exported for testing and direct use
224
- export { stripJson5, configureOpenClaw, registerScraperMcp };
269
+ export { stripJson5, configureOpenClaw, registerScraperMcp, restartOpenClawGateway };
package/lib/wizard.js CHANGED
@@ -2,11 +2,11 @@ import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { homedir, hostname } from 'node:os';
4
4
  import { readConfig, writeConfig } from '@robot-resources/cli-core/config.mjs';
5
- import { findPython, isPortAvailable, isHeadless } from './detect.js';
5
+ import { findPython, isPortAvailable, isHeadless, isOpenClawInstalled } from './detect.js';
6
6
  import { getOrCreateMachineId } from './machine-id.js';
7
7
  import { setupRouter, isRouterInstalled, getVenvPythonPath } from './python-bridge.js';
8
8
  import { installService, isServiceRunning, isServiceInstalled } from './service.js';
9
- import { configureToolRouting, registerScraperMcp } from './tool-config.js';
9
+ import { configureToolRouting, registerScraperMcp, restartOpenClawGateway } from './tool-config.js';
10
10
  import { checkHealth } from './health-report.js';
11
11
  import { header, step, success, warn, error, info, blank, summary } from './ui.js';
12
12
  /**
@@ -382,11 +382,19 @@ export async function runWizard({ nonInteractive = false } = {}) {
382
382
  }
383
383
  }
384
384
 
385
- // ── Gateway restart: NOT needed ─────────────────────────────────────────
385
+ // ── Best-effort: Single gateway restart (MUST be last) ─────────────────
386
386
  //
387
- // OC has a built-in config watcher. When openclaw.json changes (plugin
388
- // entry, scraper MCP, plugins.allow), the gateway detects the change,
389
- // waits for active operations to complete, then restarts gracefully.
390
- // No manual restart needed — the wizard just writes config and OC
391
- // handles the rest.
387
+ // Everything is already installed, configured, and reported above.
388
+ // The restart loads the plugin + scraper MCP into the running gateway.
389
+ // Telegram survives this restart. If the session dies here, the agent
390
+ // picks up on the next message with all tools loaded.
391
+
392
+ if (isOpenClawInstalled() && (results.tools?.some(r => r.action === 'installed') || scraperRegistered)) {
393
+ try {
394
+ await restartOpenClawGateway();
395
+ success('OpenClaw gateway restarted');
396
+ } catch {
397
+ // Best-effort — gateway picks up changes on next restart
398
+ }
399
+ }
392
400
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "robot-resources",
3
- "version": "1.7.6",
3
+ "version": "1.7.7",
4
4
  "description": "Robot Resources — AI agent tools. One command to install everything.",
5
5
  "type": "module",
6
6
  "bin": {