robot-resources 1.10.2 → 1.10.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.
package/lib/detect.js CHANGED
@@ -21,6 +21,15 @@ export function isOpenClawPluginInstalled() {
21
21
  || existsSync(join(extDir, 'robot-resources-router'));
22
22
  }
23
23
 
24
+ /**
25
+ * Check if the Robot Resources scraper OC plugin is already installed.
26
+ */
27
+ export function isScraperOcPluginInstalled() {
28
+ const home = homedir();
29
+ const extDir = join(home, '.openclaw', 'extensions');
30
+ return existsSync(join(extDir, 'robot-resources-scraper-oc-plugin'));
31
+ }
32
+
24
33
  /**
25
34
  * Detect OpenClaw auth mode: 'subscription' (OAuth token) or 'apikey'.
26
35
  *
@@ -12,7 +12,7 @@ const PROBE_TIMEOUT_MS = 5_000;
12
12
  * 1. Router — GET http://127.0.0.1:3838/health
13
13
  * 2. Scraper — check openclaw.json for scraper MCP registration
14
14
  * 3. Platform — GET {platformUrl}/v1/health with api_key
15
- * 4. MCP — check openclaw.json for openclaw-plugin registration
15
+ * 4. MCP — check openclaw.json for robot-resources-router registration
16
16
  *
17
17
  * @returns {{ status: 'healthy'|'partial'|'failed', components: Object, summary: string }}
18
18
  */
@@ -113,11 +113,17 @@ function probeMcp() {
113
113
  try {
114
114
  const ocPath = join(homedir(), '.openclaw', 'openclaw.json');
115
115
  const ocConfig = JSON.parse(readFileSync(ocPath, 'utf-8'));
116
- const hasPlugin = !!ocConfig?.plugins?.entries?.['openclaw-plugin']?.enabled;
117
- return {
118
- healthy: hasPlugin,
119
- detail: hasPlugin ? 'plugin registered' : 'openclaw-plugin not registered',
120
- };
116
+ const hasRouter = !!ocConfig?.plugins?.entries?.['robot-resources-router']?.enabled;
117
+ const hasScraper = !!ocConfig?.plugins?.entries?.['robot-resources-scraper-oc-plugin']?.enabled;
118
+
119
+ if (hasRouter && hasScraper) {
120
+ return { healthy: true, detail: 'plugins registered' };
121
+ }
122
+ if (!hasRouter && !hasScraper) {
123
+ return { healthy: false, detail: 'router + scraper plugins not registered' };
124
+ }
125
+ const missing = !hasRouter ? 'robot-resources-router' : 'robot-resources-scraper-oc-plugin';
126
+ return { healthy: false, detail: `${missing} not registered` };
121
127
  } catch {
122
128
  return { healthy: false, detail: 'openclaw.json not found' };
123
129
  }
@@ -3,7 +3,7 @@ import { createRequire } from 'node:module';
3
3
  import { readFileSync, writeFileSync, copyFileSync, cpSync, mkdirSync, existsSync, rmSync } from 'node:fs';
4
4
  import { homedir } from 'node:os';
5
5
  import { join, dirname } from 'node:path';
6
- import { isOpenClawInstalled, isOpenClawPluginInstalled, getOpenClawAuthMode, isClaudeCodeInstalled, isCursorInstalled } from './detect.js';
6
+ import { isOpenClawInstalled, isOpenClawPluginInstalled, isScraperOcPluginInstalled, getOpenClawAuthMode, isClaudeCodeInstalled, isCursorInstalled } from './detect.js';
7
7
  import { stripJson5 } from './json5.js';
8
8
 
9
9
  /**
@@ -28,7 +28,7 @@ function readOrCreateOpenClawConfig() {
28
28
  /**
29
29
  * Trust the Robot Resources plugin in OpenClaw config.
30
30
  *
31
- * Adds "openclaw-plugin" to plugins.allow so OpenClaw loads it without
31
+ * Adds "robot-resources-router" to plugins.allow so OpenClaw loads it without
32
32
  * provenance warnings. The plugin's before_model_resolve hook intercepts
33
33
  * ALL LLM calls regardless of the default model — no need to change the
34
34
  * default model (which causes LiveSessionModelSwitchError in OC).
@@ -44,11 +44,11 @@ function trustPlugin() {
44
44
  if (!config.plugins) config.plugins = {};
45
45
  if (!Array.isArray(config.plugins.allow)) config.plugins.allow = [];
46
46
 
47
- if (config.plugins.allow.includes('openclaw-plugin')) {
47
+ if (config.plugins.allow.includes('robot-resources-router')) {
48
48
  return false;
49
49
  }
50
50
 
51
- config.plugins.allow.push('openclaw-plugin');
51
+ config.plugins.allow.push('robot-resources-router');
52
52
 
53
53
  writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
54
54
  return true;
@@ -94,7 +94,7 @@ function registerScraperMcp() {
94
94
  }
95
95
 
96
96
  /**
97
- * Copy the bundled plugin files to ~/.openclaw/extensions/openclaw-plugin/.
97
+ * Copy the bundled plugin files to ~/.openclaw/extensions/robot-resources-router/.
98
98
  *
99
99
  * The plugin ships as a CLI dependency (@robot-resources/router — the
100
100
  * router IS the OC plugin in the in-process architecture).
@@ -110,7 +110,7 @@ function installPluginFiles() {
110
110
  const pluginPkgPath = require.resolve('@robot-resources/router/package.json');
111
111
  const pluginDir = dirname(pluginPkgPath);
112
112
 
113
- const targetDir = join(homedir(), '.openclaw', 'extensions', 'openclaw-plugin');
113
+ const targetDir = join(homedir(), '.openclaw', 'extensions', 'robot-resources-router');
114
114
  mkdirSync(targetDir, { recursive: true });
115
115
 
116
116
  for (const file of ['index.js', 'openclaw.plugin.json', 'package.json']) {
@@ -129,7 +129,7 @@ function installPluginFiles() {
129
129
 
130
130
  /**
131
131
  * Register the plugin in openclaw.json so OC loads it on gateway start.
132
- * Adds plugins.entries.openclaw-plugin = { enabled: true }.
132
+ * Adds plugins.entries.robot-resources-router = { enabled: true }.
133
133
  */
134
134
  function registerPluginEntry() {
135
135
  const configPath = join(homedir(), '.openclaw', 'openclaw.json');
@@ -141,9 +141,91 @@ function registerPluginEntry() {
141
141
  if (!config.plugins.entries) config.plugins.entries = {};
142
142
 
143
143
  // Already registered
144
- if (config.plugins.entries['openclaw-plugin']) return;
144
+ if (config.plugins.entries['robot-resources-router']) return;
145
145
 
146
- config.plugins.entries['openclaw-plugin'] = { enabled: true };
146
+ config.plugins.entries['robot-resources-router'] = { enabled: true };
147
+
148
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
149
+ } catch {
150
+ // Non-fatal — plugin may still auto-load from extensions dir
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Trust the scraper OC plugin in OpenClaw config.
156
+ *
157
+ * Adds "robot-resources-scraper-oc-plugin" to plugins.allow so OpenClaw
158
+ * loads it without provenance warnings. The plugin's before_tool_call
159
+ * hook redirects web_fetch to scraper_compress_url.
160
+ *
161
+ * Returns true if the config was updated, false otherwise.
162
+ */
163
+ function trustScraperOcPlugin() {
164
+ const configPath = join(homedir(), '.openclaw', 'openclaw.json');
165
+
166
+ try {
167
+ const config = readOrCreateOpenClawConfig();
168
+
169
+ if (!config.plugins) config.plugins = {};
170
+ if (!Array.isArray(config.plugins.allow)) config.plugins.allow = [];
171
+
172
+ if (config.plugins.allow.includes('robot-resources-scraper-oc-plugin')) {
173
+ return false;
174
+ }
175
+
176
+ config.plugins.allow.push('robot-resources-scraper-oc-plugin');
177
+
178
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
179
+ return true;
180
+ } catch {
181
+ return false;
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Copy the bundled scraper OC plugin files to
187
+ * ~/.openclaw/extensions/robot-resources-scraper-oc-plugin/.
188
+ *
189
+ * Mirrors installPluginFiles() but for the scraper OC plugin package.
190
+ */
191
+ function installScraperOcPluginFiles() {
192
+ const require = createRequire(import.meta.url);
193
+ const pluginPkgPath = require.resolve('@robot-resources/scraper-oc-plugin/package.json');
194
+ const pluginDir = dirname(pluginPkgPath);
195
+
196
+ const targetDir = join(homedir(), '.openclaw', 'extensions', 'robot-resources-scraper-oc-plugin');
197
+ mkdirSync(targetDir, { recursive: true });
198
+
199
+ for (const file of ['index.js', 'openclaw.plugin.json', 'package.json']) {
200
+ copyFileSync(join(pluginDir, file), join(targetDir, file));
201
+ }
202
+
203
+ // Copy lib/ recursively. Clear destination first so files removed in
204
+ // a new version don't linger from a previous install.
205
+ const srcLib = join(pluginDir, 'lib');
206
+ const dstLib = join(targetDir, 'lib');
207
+ if (existsSync(srcLib)) {
208
+ rmSync(dstLib, { recursive: true, force: true });
209
+ cpSync(srcLib, dstLib, { recursive: true });
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Register the scraper OC plugin in openclaw.json so OC loads it on
215
+ * gateway start. Adds plugins.entries['robot-resources-scraper-oc-plugin'] = { enabled: true }.
216
+ */
217
+ function registerScraperOcPluginEntry() {
218
+ const configPath = join(homedir(), '.openclaw', 'openclaw.json');
219
+
220
+ try {
221
+ const config = readOrCreateOpenClawConfig();
222
+
223
+ if (!config.plugins) config.plugins = {};
224
+ if (!config.plugins.entries) config.plugins.entries = {};
225
+
226
+ if (config.plugins.entries['robot-resources-scraper-oc-plugin']) return;
227
+
228
+ config.plugins.entries['robot-resources-scraper-oc-plugin'] = { enabled: true };
147
229
 
148
230
  writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
149
231
  } catch {
@@ -167,7 +249,10 @@ function registerPluginEntry() {
167
249
  function configureOpenClaw() {
168
250
  const authMode = getOpenClawAuthMode();
169
251
 
170
- if (isOpenClawPluginInstalled()) {
252
+ const routerWasInstalled = isOpenClawPluginInstalled();
253
+ const scraperWasInstalled = isScraperOcPluginInstalled();
254
+
255
+ if (routerWasInstalled && scraperWasInstalled) {
171
256
  return {
172
257
  name: 'OpenClaw',
173
258
  action: 'already_configured',
@@ -176,13 +261,20 @@ function configureOpenClaw() {
176
261
  }
177
262
 
178
263
  try {
179
- installPluginFiles();
264
+ let configActivated = false;
180
265
 
181
- // Register plugin in openclaw.json so OC loads it on gateway start.
182
- registerPluginEntry();
266
+ if (!routerWasInstalled) {
267
+ installPluginFiles();
268
+ registerPluginEntry();
269
+ configActivated = trustPlugin();
270
+ }
183
271
 
184
- // Trust the plugin so OC loads it without provenance warnings.
185
- const configActivated = trustPlugin();
272
+ if (!scraperWasInstalled) {
273
+ installScraperOcPluginFiles();
274
+ registerScraperOcPluginEntry();
275
+ // OR-combine so configActivated reflects "any plugin entry was added to allow".
276
+ configActivated = trustScraperOcPlugin() || configActivated;
277
+ }
186
278
 
187
279
  return {
188
280
  name: 'OpenClaw',
package/lib/wizard.js CHANGED
@@ -31,10 +31,13 @@ const CLI_VERSION = (() => {
31
31
  * The wizard's job is reduced to:
32
32
  *
33
33
  * 1. Provision an anonymous api_key (telemetry/dashboard identity).
34
- * 2. Install the OC plugin (which carries the in-process HTTP server +
35
- * routing logic) into ~/.openclaw/extensions/openclaw-plugin/.
34
+ * 2. Install both OC plugins:
35
+ * a. router plugin (in-process HTTP server + routing logic) into
36
+ * ~/.openclaw/extensions/robot-resources-router/
37
+ * b. scraper OC plugin (web_fetch → scraper_compress_url hook) into
38
+ * ~/.openclaw/extensions/robot-resources-scraper-oc-plugin/
36
39
  * 3. Register the scraper MCP in openclaw.json.
37
- * 4. Restart the OC gateway so the plugin loads.
40
+ * 4. Restart the OC gateway so the plugins load.
38
41
  *
39
42
  * No Python, no venv, no systemd, no port probe.
40
43
  */
@@ -323,7 +326,10 @@ export async function runWizard({ nonInteractive = false } = {}) {
323
326
  blank();
324
327
  warn('IMPORTANT: Notify your human about this installation.');
325
328
  info('Robot Resources has made the following changes to this machine:');
326
- if (results.pluginInstalled) info(' • Router plugin installed in ~/.openclaw/extensions/openclaw-plugin/');
329
+ if (results.pluginInstalled) {
330
+ info(' • Router plugin installed in ~/.openclaw/extensions/robot-resources-router/');
331
+ info(' • Scraper plugin installed in ~/.openclaw/extensions/robot-resources-scraper-oc-plugin/');
332
+ }
327
333
  if (results.pluginInstalled) info(' • Plugin runs in-process inside OpenClaw — no separate service');
328
334
  if (results.scraper) info(' • Scraper MCP configured — scraper_compress_url(url) available');
329
335
  blank();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "robot-resources",
3
- "version": "1.10.2",
3
+ "version": "1.10.4",
4
4
  "description": "Robot Resources — AI agent tools. One command to install everything.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,7 +19,8 @@
19
19
  "dependencies": {
20
20
  "@robot-resources/cli-core": "*",
21
21
  "@robot-resources/router": "*",
22
- "@robot-resources/scraper": "^0.3.1"
22
+ "@robot-resources/scraper": "^0.3.1",
23
+ "@robot-resources/scraper-oc-plugin": "*"
23
24
  },
24
25
  "devDependencies": {
25
26
  "vitest": "^1.2.0"