robot-resources 1.4.0 → 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.
- package/lib/tool-config.js +2 -7
- package/lib/wizard.js +105 -5
- package/package.json +1 -1
package/lib/tool-config.js
CHANGED
|
@@ -114,10 +114,6 @@ function configureOpenClaw() {
|
|
|
114
114
|
// before_model_resolve hook actually fires for every request.
|
|
115
115
|
const configActivated = activateRouterModel();
|
|
116
116
|
|
|
117
|
-
// Register scraper-mcp so the agent gets scraper_compress_url
|
|
118
|
-
// as a native tool, and the plugin can intercept web_fetch.
|
|
119
|
-
const scraperRegistered = registerScraperMcp();
|
|
120
|
-
|
|
121
117
|
// Restart the gateway so it picks up the new plugin + config.
|
|
122
118
|
let gatewayRestarted = false;
|
|
123
119
|
try {
|
|
@@ -135,7 +131,6 @@ function configureOpenClaw() {
|
|
|
135
131
|
action: 'installed',
|
|
136
132
|
authMode,
|
|
137
133
|
configActivated,
|
|
138
|
-
scraperRegistered,
|
|
139
134
|
gatewayRestarted,
|
|
140
135
|
note: authMode === 'subscription'
|
|
141
136
|
? 'Plugin required — subscription OAuth tokens are rejected by Anthropic when proxied via third-party clients.'
|
|
@@ -183,5 +178,5 @@ export function configureToolRouting() {
|
|
|
183
178
|
return results;
|
|
184
179
|
}
|
|
185
180
|
|
|
186
|
-
// Exported for testing
|
|
187
|
-
export { stripJson5, configureOpenClaw };
|
|
181
|
+
// Exported for testing and direct use
|
|
182
|
+
export { stripJson5, configureOpenClaw, registerScraperMcp };
|
package/lib/wizard.js
CHANGED
|
@@ -2,7 +2,7 @@ import { readConfig, writeConfig } from '@robot-resources/cli-core/config.mjs';
|
|
|
2
2
|
import { findPython, isPortAvailable, isHeadless } from './detect.js';
|
|
3
3
|
import { setupRouter, isRouterInstalled, getVenvPythonPath } from './python-bridge.js';
|
|
4
4
|
import { installService, isServiceRunning, isServiceInstalled } from './service.js';
|
|
5
|
-
import { configureToolRouting } from './tool-config.js';
|
|
5
|
+
import { configureToolRouting, registerScraperMcp } from './tool-config.js';
|
|
6
6
|
import { header, step, success, warn, error, info, blank, summary } from './ui.js';
|
|
7
7
|
/**
|
|
8
8
|
* Main setup wizard. Handles the full onboarding flow:
|
|
@@ -201,8 +201,104 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
// ── Step 4
|
|
204
|
+
// ── Step 4: Scraper Installation ───────────────────────────────────────
|
|
205
|
+
//
|
|
206
|
+
// Independent of router. Scraper works even if router failed to install.
|
|
207
|
+
// 1. Register scraper-mcp in openclaw.json (if OC is present)
|
|
208
|
+
// 2. Pre-cache the scraper-mcp npm package
|
|
209
|
+
// 3. Restart gateway so OC picks up the new MCP server
|
|
210
|
+
|
|
211
|
+
blank();
|
|
212
|
+
step('Installing Scraper...');
|
|
213
|
+
|
|
214
|
+
results.scraper = false;
|
|
205
215
|
|
|
216
|
+
// Register MCP in openclaw.json
|
|
217
|
+
const scraperRegistered = registerScraperMcp();
|
|
218
|
+
if (scraperRegistered) {
|
|
219
|
+
success('Scraper MCP registered in OpenClaw — scraper_compress_url(url) available');
|
|
220
|
+
} else {
|
|
221
|
+
// Either already registered, or no openclaw.json
|
|
222
|
+
try {
|
|
223
|
+
const { readFileSync: readFs } = await import('node:fs');
|
|
224
|
+
const { join: joinPath } = await import('node:path');
|
|
225
|
+
const { homedir: home } = await import('node:os');
|
|
226
|
+
const ocConfig = JSON.parse(readFs(joinPath(home(), '.openclaw', 'openclaw.json'), 'utf-8'));
|
|
227
|
+
if (ocConfig?.mcp?.servers?.['robot-resources-scraper']) {
|
|
228
|
+
success('Scraper MCP already registered in OpenClaw');
|
|
229
|
+
}
|
|
230
|
+
} catch {
|
|
231
|
+
// No openclaw.json — not on OC, skip
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Pre-cache scraper-mcp package so OC doesn't need to download on first use
|
|
236
|
+
try {
|
|
237
|
+
const { execFileSync: execFs } = await import('node:child_process');
|
|
238
|
+
execFs('npx', ['-y', '@robot-resources/scraper-mcp', '--help'], {
|
|
239
|
+
stdio: 'ignore',
|
|
240
|
+
timeout: 60_000,
|
|
241
|
+
});
|
|
242
|
+
success('Scraper MCP package cached');
|
|
243
|
+
results.scraper = true;
|
|
244
|
+
} catch {
|
|
245
|
+
warn('Scraper MCP pre-cache failed — will download on first use');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Restart gateway so OC picks up the scraper MCP server
|
|
249
|
+
if (scraperRegistered) {
|
|
250
|
+
try {
|
|
251
|
+
const { execFileSync: execFs2 } = await import('node:child_process');
|
|
252
|
+
execFs2('openclaw', ['gateway', 'restart'], {
|
|
253
|
+
stdio: 'ignore',
|
|
254
|
+
timeout: 15_000,
|
|
255
|
+
});
|
|
256
|
+
success('OpenClaw gateway restarted');
|
|
257
|
+
results.scraper = true;
|
|
258
|
+
} catch {
|
|
259
|
+
// Non-fatal — gateway may not be running
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ── Step 4.5: Healthchecks ────────────────────────────────────────────
|
|
264
|
+
|
|
265
|
+
// Scraper MCP: verify the server starts without crashing
|
|
266
|
+
if (results.scraper) {
|
|
267
|
+
step('Verifying Scraper MCP starts...');
|
|
268
|
+
try {
|
|
269
|
+
const { spawn } = await import('node:child_process');
|
|
270
|
+
const proc = spawn('npx', ['-y', '@robot-resources/scraper-mcp'], {
|
|
271
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
272
|
+
timeout: 10_000,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
const healthy = await new Promise((resolve) => {
|
|
276
|
+
const timer = setTimeout(() => {
|
|
277
|
+
// Server stayed alive for 3s — it's working
|
|
278
|
+
proc.kill();
|
|
279
|
+
resolve(true);
|
|
280
|
+
}, 3000);
|
|
281
|
+
|
|
282
|
+
proc.on('error', () => { clearTimeout(timer); resolve(false); });
|
|
283
|
+
proc.on('exit', (code) => {
|
|
284
|
+
clearTimeout(timer);
|
|
285
|
+
// MCP servers don't exit on their own — if it exited, it crashed
|
|
286
|
+
resolve(code === 0);
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
if (healthy) {
|
|
291
|
+
success('Scraper MCP server healthy');
|
|
292
|
+
} else {
|
|
293
|
+
warn('Scraper MCP server exited unexpectedly');
|
|
294
|
+
results.scraper = false;
|
|
295
|
+
}
|
|
296
|
+
} catch {
|
|
297
|
+
warn('Could not verify Scraper MCP server');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Router: verify it's responding on localhost:3838
|
|
206
302
|
if (results.service) {
|
|
207
303
|
blank();
|
|
208
304
|
step('Verifying Router is responding...');
|
|
@@ -236,7 +332,7 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
236
332
|
|
|
237
333
|
// ── Summary ─────────────────────────────────────────────────────────────
|
|
238
334
|
|
|
239
|
-
const somethingInstalled = results.router || results.service;
|
|
335
|
+
const somethingInstalled = results.router || results.service || results.scraper;
|
|
240
336
|
|
|
241
337
|
const lines = [];
|
|
242
338
|
|
|
@@ -251,7 +347,11 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
251
347
|
lines.push('○ Router not installed (Python 3.10+ required)');
|
|
252
348
|
}
|
|
253
349
|
|
|
254
|
-
|
|
350
|
+
if (results.scraper) {
|
|
351
|
+
lines.push('✓ Scraper MCP ready — use scraper_compress_url(url) to compress web content');
|
|
352
|
+
} else {
|
|
353
|
+
lines.push('○ Scraper MCP not configured');
|
|
354
|
+
}
|
|
255
355
|
|
|
256
356
|
summary(lines);
|
|
257
357
|
|
|
@@ -276,7 +376,7 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
276
376
|
if (results.router) info(' • Router installed in ~/.robot-resources/');
|
|
277
377
|
if (results.service) info(' • Router registered as a transparent proxy (localhost:3838)');
|
|
278
378
|
if (results.service) info(' • Reads API keys from requests — no keys stored by Router');
|
|
279
|
-
info(' • Scraper
|
|
379
|
+
if (results.scraper) info(' • Scraper MCP configured — scraper_compress_url(url) available');
|
|
280
380
|
blank();
|
|
281
381
|
const claimLink = results.claimUrl || 'https://robotresources.ai/dashboard';
|
|
282
382
|
info('Suggested message to your human (COPY THIS EXACTLY):');
|