robot-resources 1.9.1 → 1.9.2
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/wizard.js +49 -7
- package/package.json +1 -1
package/lib/wizard.js
CHANGED
|
@@ -9,6 +9,40 @@ import { installService, isServiceRunning, isServiceInstalled } from './service.
|
|
|
9
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
|
+
/**
|
|
13
|
+
* Classify an install error into a short reason code + bounded detail string.
|
|
14
|
+
*
|
|
15
|
+
* Before this existed, install_complete telemetry reported router:false with
|
|
16
|
+
* no context — 100% of rr-router installs failed and we couldn't diagnose.
|
|
17
|
+
* The reason code slots into a small enum so we can aggregate in the admin
|
|
18
|
+
* dashboard; detail is the tail of stderr/error message for deep-dives.
|
|
19
|
+
*/
|
|
20
|
+
function classifyRouterError(err) {
|
|
21
|
+
const msg = (err?.message || String(err)).toLowerCase();
|
|
22
|
+
let reason = 'unknown';
|
|
23
|
+
|
|
24
|
+
if (msg.includes('python 3.10+') || msg.includes('python is required')) {
|
|
25
|
+
reason = 'python_not_found';
|
|
26
|
+
} else if (err?.code === 'ENOENT' || msg.includes('enoent')) {
|
|
27
|
+
reason = 'spawn_enoent';
|
|
28
|
+
} else if (msg.includes('timeout') || msg.includes('timed out') || err?.code === 'ETIMEDOUT') {
|
|
29
|
+
reason = 'timeout';
|
|
30
|
+
} else if (msg.includes('exited with code') || msg.includes('pip install')) {
|
|
31
|
+
reason = 'pip_install_failed';
|
|
32
|
+
} else if (msg.includes('permission denied') || err?.code === 'EACCES') {
|
|
33
|
+
reason = 'permission_denied';
|
|
34
|
+
} else if (msg.includes('disk') || msg.includes('space') || err?.code === 'ENOSPC') {
|
|
35
|
+
reason = 'disk_full';
|
|
36
|
+
} else if (msg.includes('network') || msg.includes('getaddrinfo') || msg.includes('enetunreach')) {
|
|
37
|
+
reason = 'network';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const rawDetail = err?.stderr?.trim?.() || err?.message || String(err);
|
|
41
|
+
const detail = rawDetail.slice(-500);
|
|
42
|
+
|
|
43
|
+
return { reason, detail, exitCode: err?.exitCode ?? null };
|
|
44
|
+
}
|
|
45
|
+
|
|
12
46
|
/**
|
|
13
47
|
* Main setup wizard. Handles the full onboarding flow:
|
|
14
48
|
* 1. Router installation (Python venv + pip)
|
|
@@ -128,6 +162,8 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
128
162
|
warn('Python 3.10+ not found — skipping Router installation');
|
|
129
163
|
info('Install Python from https://python.org and re-run this wizard');
|
|
130
164
|
info('Scraper works without Python');
|
|
165
|
+
// Record the reason so install_complete tells us why router=false.
|
|
166
|
+
results.routerError = { reason: 'python_not_found', detail: 'Python 3.10+ not detected on PATH' };
|
|
131
167
|
} else {
|
|
132
168
|
info(`Found Python ${python.version} (${python.bin})`);
|
|
133
169
|
step('Installing Router (this may take a moment)...');
|
|
@@ -138,7 +174,7 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
138
174
|
results.router = true;
|
|
139
175
|
} catch (err) {
|
|
140
176
|
error(`Router installation failed: ${err.message}`);
|
|
141
|
-
results.routerError =
|
|
177
|
+
results.routerError = classifyRouterError(err);
|
|
142
178
|
}
|
|
143
179
|
}
|
|
144
180
|
}
|
|
@@ -298,15 +334,21 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
298
334
|
try {
|
|
299
335
|
const config = readConfig();
|
|
300
336
|
const platformUrl = process.env.RR_PLATFORM_URL || 'https://api.robotresources.ai';
|
|
337
|
+
const installPayload = {
|
|
338
|
+
router: results.router || false,
|
|
339
|
+
service: results.service || false,
|
|
340
|
+
scraper: results.scraper || false,
|
|
341
|
+
source: 'wizard',
|
|
342
|
+
};
|
|
343
|
+
if (results.routerError && typeof results.routerError === 'object') {
|
|
344
|
+
installPayload.routerError = results.routerError.reason;
|
|
345
|
+
installPayload.routerErrorDetail = results.routerError.detail;
|
|
346
|
+
installPayload.platform = process.platform;
|
|
347
|
+
}
|
|
301
348
|
const body = JSON.stringify({
|
|
302
349
|
product: 'cli',
|
|
303
350
|
event_type: 'install_complete',
|
|
304
|
-
payload:
|
|
305
|
-
router: results.router || false,
|
|
306
|
-
service: results.service || false,
|
|
307
|
-
scraper: results.scraper || false,
|
|
308
|
-
source: 'wizard',
|
|
309
|
-
},
|
|
351
|
+
payload: installPayload,
|
|
310
352
|
});
|
|
311
353
|
|
|
312
354
|
for (let attempt = 0; attempt < 2; attempt++) {
|