robot-resources 1.4.1 → 1.5.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 +39 -9
- package/lib/wizard.js +19 -21
- package/package.json +2 -2
package/lib/tool-config.js
CHANGED
|
@@ -1,10 +1,40 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
2
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { isOpenClawInstalled, isOpenClawPluginInstalled, getOpenClawAuthMode } from './detect.js';
|
|
6
6
|
import { stripJson5 } from './json5.js';
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Run a command with a heartbeat that prints progress every 5 seconds.
|
|
10
|
+
* Prevents agent sessions from timing out during silent operations.
|
|
11
|
+
*/
|
|
12
|
+
function spawnWithHeartbeat(cmd, args, { label, timeout = 30_000 } = {}) {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const proc = spawn(cmd, args, {
|
|
15
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
16
|
+
timeout,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
let seconds = 0;
|
|
20
|
+
const heartbeat = setInterval(() => {
|
|
21
|
+
seconds += 5;
|
|
22
|
+
process.stdout.write(` ${label}... ${seconds}s\n`);
|
|
23
|
+
}, 5000);
|
|
24
|
+
|
|
25
|
+
proc.on('close', (code) => {
|
|
26
|
+
clearInterval(heartbeat);
|
|
27
|
+
if (code === 0) resolve();
|
|
28
|
+
else reject(new Error(`${cmd} ${args.join(' ')} exited with code ${code}`));
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
proc.on('error', (err) => {
|
|
32
|
+
clearInterval(heartbeat);
|
|
33
|
+
reject(err);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
8
38
|
/**
|
|
9
39
|
* Set robot-resources/auto as the default model in openclaw.json.
|
|
10
40
|
*
|
|
@@ -69,7 +99,7 @@ function registerScraperMcp() {
|
|
|
69
99
|
|
|
70
100
|
config.mcp.servers['robot-resources-scraper'] = {
|
|
71
101
|
command: 'npx',
|
|
72
|
-
args: ['-y', '@robot-resources/scraper-mcp'],
|
|
102
|
+
args: ['-y', '-p', '@robot-resources/scraper', 'scraper-mcp'],
|
|
73
103
|
};
|
|
74
104
|
|
|
75
105
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
@@ -93,7 +123,7 @@ function registerScraperMcp() {
|
|
|
93
123
|
* OAuth tokens from third-party clients, so HTTP proxy won't work.
|
|
94
124
|
* - apikey: Plugin is preferred (survives restarts) but proxy also works.
|
|
95
125
|
*/
|
|
96
|
-
function configureOpenClaw() {
|
|
126
|
+
async function configureOpenClaw() {
|
|
97
127
|
const authMode = getOpenClawAuthMode();
|
|
98
128
|
|
|
99
129
|
if (isOpenClawPluginInstalled()) {
|
|
@@ -105,8 +135,8 @@ function configureOpenClaw() {
|
|
|
105
135
|
}
|
|
106
136
|
|
|
107
137
|
try {
|
|
108
|
-
|
|
109
|
-
|
|
138
|
+
await spawnWithHeartbeat('openclaw', ['plugins', 'install', '@robot-resources/openclaw-plugin'], {
|
|
139
|
+
label: 'Installing plugin',
|
|
110
140
|
timeout: 30_000,
|
|
111
141
|
});
|
|
112
142
|
|
|
@@ -117,8 +147,8 @@ function configureOpenClaw() {
|
|
|
117
147
|
// Restart the gateway so it picks up the new plugin + config.
|
|
118
148
|
let gatewayRestarted = false;
|
|
119
149
|
try {
|
|
120
|
-
|
|
121
|
-
|
|
150
|
+
await spawnWithHeartbeat('openclaw', ['gateway', 'restart'], {
|
|
151
|
+
label: 'Restarting gateway',
|
|
122
152
|
timeout: 15_000,
|
|
123
153
|
});
|
|
124
154
|
gatewayRestarted = true;
|
|
@@ -167,12 +197,12 @@ function configureOpenClaw() {
|
|
|
167
197
|
*
|
|
168
198
|
* Returns array of { name, action, ... } results.
|
|
169
199
|
*/
|
|
170
|
-
export function configureToolRouting() {
|
|
200
|
+
export async function configureToolRouting() {
|
|
171
201
|
const results = [];
|
|
172
202
|
|
|
173
203
|
// OpenClaw
|
|
174
204
|
if (isOpenClawInstalled()) {
|
|
175
|
-
results.push(configureOpenClaw());
|
|
205
|
+
results.push(await configureOpenClaw());
|
|
176
206
|
}
|
|
177
207
|
|
|
178
208
|
return results;
|
package/lib/wizard.js
CHANGED
|
@@ -172,7 +172,7 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
172
172
|
blank();
|
|
173
173
|
step('Configuring AI tools to use Router...');
|
|
174
174
|
|
|
175
|
-
const toolResults = configureToolRouting();
|
|
175
|
+
const toolResults = await configureToolRouting();
|
|
176
176
|
results.tools = toolResults;
|
|
177
177
|
|
|
178
178
|
if (toolResults.length === 0) {
|
|
@@ -204,9 +204,9 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
204
204
|
// ── Step 4: Scraper Installation ───────────────────────────────────────
|
|
205
205
|
//
|
|
206
206
|
// Independent of router. Scraper works even if router failed to install.
|
|
207
|
-
// 1. Register scraper
|
|
208
|
-
// 2.
|
|
209
|
-
//
|
|
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
210
|
|
|
211
211
|
blank();
|
|
212
212
|
step('Installing Scraper...');
|
|
@@ -232,26 +232,24 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
|
|
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
|
-
}
|
|
235
|
+
results.scraper = true;
|
|
247
236
|
|
|
248
237
|
// Restart gateway so OC picks up the scraper MCP server
|
|
249
238
|
if (scraperRegistered) {
|
|
250
239
|
try {
|
|
251
|
-
const {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
240
|
+
const { spawn: spawnProc } = await import('node:child_process');
|
|
241
|
+
await new Promise((resolve, reject) => {
|
|
242
|
+
const proc = spawnProc('openclaw', ['gateway', 'restart'], {
|
|
243
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
244
|
+
timeout: 15_000,
|
|
245
|
+
});
|
|
246
|
+
let seconds = 0;
|
|
247
|
+
const hb = setInterval(() => {
|
|
248
|
+
seconds += 5;
|
|
249
|
+
process.stdout.write(` Restarting gateway... ${seconds}s\n`);
|
|
250
|
+
}, 5000);
|
|
251
|
+
proc.on('close', (code) => { clearInterval(hb); code === 0 ? resolve() : reject(new Error(`exit ${code}`)); });
|
|
252
|
+
proc.on('error', (err) => { clearInterval(hb); reject(err); });
|
|
255
253
|
});
|
|
256
254
|
success('OpenClaw gateway restarted');
|
|
257
255
|
results.scraper = true;
|
|
@@ -267,7 +265,7 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
267
265
|
step('Verifying Scraper MCP starts...');
|
|
268
266
|
try {
|
|
269
267
|
const { spawn } = await import('node:child_process');
|
|
270
|
-
const proc = spawn('npx', ['-y', '@robot-resources/scraper-mcp'], {
|
|
268
|
+
const proc = spawn('npx', ['-y', '-p', '@robot-resources/scraper', 'scraper-mcp'], {
|
|
271
269
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
272
270
|
timeout: 10_000,
|
|
273
271
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "robot-resources",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
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"
|