robot-resources 1.4.0 → 1.5.0
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 +3 -8
- package/lib/wizard.js +94 -5
- package/package.json +2 -2
package/lib/tool-config.js
CHANGED
|
@@ -69,7 +69,7 @@ function registerScraperMcp() {
|
|
|
69
69
|
|
|
70
70
|
config.mcp.servers['robot-resources-scraper'] = {
|
|
71
71
|
command: 'npx',
|
|
72
|
-
args: ['-y', '@robot-resources/scraper-mcp'],
|
|
72
|
+
args: ['-y', '-p', '@robot-resources/scraper', 'scraper-mcp'],
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
@@ -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,93 @@ 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. Restart gateway so OC picks up the new MCP server
|
|
209
|
+
// No pre-cache needed — scraper is bundled as a CLI dependency
|
|
210
|
+
|
|
211
|
+
blank();
|
|
212
|
+
step('Installing Scraper...');
|
|
213
|
+
|
|
214
|
+
results.scraper = false;
|
|
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
|
+
}
|
|
205
234
|
|
|
235
|
+
results.scraper = true;
|
|
236
|
+
|
|
237
|
+
// Restart gateway so OC picks up the scraper MCP server
|
|
238
|
+
if (scraperRegistered) {
|
|
239
|
+
try {
|
|
240
|
+
const { execFileSync: execFs2 } = await import('node:child_process');
|
|
241
|
+
execFs2('openclaw', ['gateway', 'restart'], {
|
|
242
|
+
stdio: 'ignore',
|
|
243
|
+
timeout: 15_000,
|
|
244
|
+
});
|
|
245
|
+
success('OpenClaw gateway restarted');
|
|
246
|
+
results.scraper = true;
|
|
247
|
+
} catch {
|
|
248
|
+
// Non-fatal — gateway may not be running
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ── Step 4.5: Healthchecks ────────────────────────────────────────────
|
|
253
|
+
|
|
254
|
+
// Scraper MCP: verify the server starts without crashing
|
|
255
|
+
if (results.scraper) {
|
|
256
|
+
step('Verifying Scraper MCP starts...');
|
|
257
|
+
try {
|
|
258
|
+
const { spawn } = await import('node:child_process');
|
|
259
|
+
const proc = spawn('npx', ['-y', '-p', '@robot-resources/scraper', 'scraper-mcp'], {
|
|
260
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
261
|
+
timeout: 10_000,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const healthy = await new Promise((resolve) => {
|
|
265
|
+
const timer = setTimeout(() => {
|
|
266
|
+
// Server stayed alive for 3s — it's working
|
|
267
|
+
proc.kill();
|
|
268
|
+
resolve(true);
|
|
269
|
+
}, 3000);
|
|
270
|
+
|
|
271
|
+
proc.on('error', () => { clearTimeout(timer); resolve(false); });
|
|
272
|
+
proc.on('exit', (code) => {
|
|
273
|
+
clearTimeout(timer);
|
|
274
|
+
// MCP servers don't exit on their own — if it exited, it crashed
|
|
275
|
+
resolve(code === 0);
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
if (healthy) {
|
|
280
|
+
success('Scraper MCP server healthy');
|
|
281
|
+
} else {
|
|
282
|
+
warn('Scraper MCP server exited unexpectedly');
|
|
283
|
+
results.scraper = false;
|
|
284
|
+
}
|
|
285
|
+
} catch {
|
|
286
|
+
warn('Could not verify Scraper MCP server');
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Router: verify it's responding on localhost:3838
|
|
206
291
|
if (results.service) {
|
|
207
292
|
blank();
|
|
208
293
|
step('Verifying Router is responding...');
|
|
@@ -236,7 +321,7 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
236
321
|
|
|
237
322
|
// ── Summary ─────────────────────────────────────────────────────────────
|
|
238
323
|
|
|
239
|
-
const somethingInstalled = results.router || results.service;
|
|
324
|
+
const somethingInstalled = results.router || results.service || results.scraper;
|
|
240
325
|
|
|
241
326
|
const lines = [];
|
|
242
327
|
|
|
@@ -251,7 +336,11 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
251
336
|
lines.push('○ Router not installed (Python 3.10+ required)');
|
|
252
337
|
}
|
|
253
338
|
|
|
254
|
-
|
|
339
|
+
if (results.scraper) {
|
|
340
|
+
lines.push('✓ Scraper MCP ready — use scraper_compress_url(url) to compress web content');
|
|
341
|
+
} else {
|
|
342
|
+
lines.push('○ Scraper MCP not configured');
|
|
343
|
+
}
|
|
255
344
|
|
|
256
345
|
summary(lines);
|
|
257
346
|
|
|
@@ -276,7 +365,7 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
276
365
|
if (results.router) info(' • Router installed in ~/.robot-resources/');
|
|
277
366
|
if (results.service) info(' • Router registered as a transparent proxy (localhost:3838)');
|
|
278
367
|
if (results.service) info(' • Reads API keys from requests — no keys stored by Router');
|
|
279
|
-
info(' • Scraper
|
|
368
|
+
if (results.scraper) info(' • Scraper MCP configured — scraper_compress_url(url) available');
|
|
280
369
|
blank();
|
|
281
370
|
const claimLink = results.claimUrl || 'https://robotresources.ai/dashboard';
|
|
282
371
|
info('Suggested message to your human (COPY THIS EXACTLY):');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "robot-resources",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Robot Resources — AI agent runtime tools. One command to install everything.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@robot-resources/cli-core": "*",
|
|
21
|
-
"@robot-resources/scraper": "^0.
|
|
21
|
+
"@robot-resources/scraper": "^0.2.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"vitest": "^1.2.0"
|