robot-resources 1.11.1 → 1.11.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/bin/setup.js +26 -14
- package/lib/non-oc-wizard.js +8 -2
- package/lib/uninstall.js +100 -0
- package/lib/wizard.js +69 -0
- package/package.json +1 -1
package/bin/setup.js
CHANGED
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { runWizard } from '../lib/wizard.js';
|
|
3
|
+
import { runWizard, runUninstallCommand } from '../lib/wizard.js';
|
|
4
4
|
|
|
5
5
|
const args = process.argv.slice(2);
|
|
6
|
-
const explicitNonInteractive =
|
|
7
|
-
args.includes('--non-interactive') || args.includes('--yes') || args.includes('-y');
|
|
8
|
-
const targetArg = args.find((a) => a.startsWith('--for='));
|
|
9
|
-
const target = targetArg ? targetArg.slice('--for='.length) : null;
|
|
10
6
|
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
const
|
|
7
|
+
// --uninstall ships in Phase 0 as the counterpart to install. --purge also
|
|
8
|
+
// wipes ~/.robot-resources/config.json (api_key + claim_url); without it,
|
|
9
|
+
// the api_key is preserved across a reinstall.
|
|
10
|
+
if (args.includes('--uninstall')) {
|
|
11
|
+
const purge = args.includes('--purge');
|
|
12
|
+
runUninstallCommand({ purge }).catch((err) => {
|
|
13
|
+
console.error(`\n ✗ Uninstall failed: ${err.message}\n`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
});
|
|
16
|
+
} else {
|
|
17
|
+
const explicitNonInteractive =
|
|
18
|
+
args.includes('--non-interactive') || args.includes('--yes') || args.includes('-y');
|
|
19
|
+
const targetArg = args.find((a) => a.startsWith('--for='));
|
|
20
|
+
const target = targetArg ? targetArg.slice('--for='.length) : null;
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
// Treat piped/CI runs (no TTY on stdin OR stdout) as non-interactive so the
|
|
23
|
+
// wizard never blocks on a prompt that can't be answered. The interactive
|
|
24
|
+
// menu is only opened when both stdin and stdout are real terminals.
|
|
25
|
+
const hasTty = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
26
|
+
const nonInteractive = explicitNonInteractive || !hasTty;
|
|
27
|
+
|
|
28
|
+
runWizard({ nonInteractive, target }).catch((err) => {
|
|
29
|
+
console.error(`\n ✗ Setup failed: ${err.message}\n`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
});
|
|
32
|
+
}
|
package/lib/non-oc-wizard.js
CHANGED
|
@@ -206,6 +206,7 @@ export async function runNonOcWizard({ nonInteractive = false, target = null } =
|
|
|
206
206
|
info(' npx robot-resources --for=claude-code # Claude Code MCP config');
|
|
207
207
|
info(' npx robot-resources --for=docs # docs URL');
|
|
208
208
|
blank();
|
|
209
|
+
await emitPathChosen('noninteractive_no_target');
|
|
209
210
|
return;
|
|
210
211
|
}
|
|
211
212
|
|
|
@@ -231,8 +232,13 @@ export async function runNonOcWizard({ nonInteractive = false, target = null } =
|
|
|
231
232
|
],
|
|
232
233
|
});
|
|
233
234
|
} catch (err) {
|
|
234
|
-
// User hit Ctrl-C or terminal closed — exit cleanly
|
|
235
|
-
|
|
235
|
+
// User hit Ctrl-C or terminal closed — exit cleanly, but mark the funnel
|
|
236
|
+
// so we can distinguish "agent shown the prompt and bailed" from
|
|
237
|
+
// "wizard never reached the prompt at all" in Supabase.
|
|
238
|
+
if (err && (err.name === 'ExitPromptError' || err.code === 'ABORT_ERR')) {
|
|
239
|
+
await emitPathChosen('aborted');
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
236
242
|
throw err;
|
|
237
243
|
}
|
|
238
244
|
|
package/lib/uninstall.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { stripJson5 } from './json5.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Single source of truth for `npx robot-resources --uninstall`.
|
|
8
|
+
*
|
|
9
|
+
* Reverses the install actions in tool-config.js: removes the router and
|
|
10
|
+
* scraper OC plugin directories, deletes their entries from openclaw.json
|
|
11
|
+
* (plugins.entries + plugins.allow + mcp.servers).
|
|
12
|
+
*
|
|
13
|
+
* Phase 0 scope is OC-only. Phase 3 will extend this with shell-config
|
|
14
|
+
* removal (NODE_OPTIONS line) and `pip uninstall robot-resources` for the
|
|
15
|
+
* Node and Python shim install paths.
|
|
16
|
+
*
|
|
17
|
+
* `~/.robot-resources/config.json` is preserved by default so a subsequent
|
|
18
|
+
* re-install reuses the same api_key (and the user's claim_url stays valid).
|
|
19
|
+
* Pass { purge: true } to wipe it as well.
|
|
20
|
+
*
|
|
21
|
+
* Returns { components_removed: string[], errors: { component, message }[] }
|
|
22
|
+
* for telemetry. Failure to remove one component never aborts the others —
|
|
23
|
+
* a partial uninstall is still progress, and we want to record what worked.
|
|
24
|
+
*/
|
|
25
|
+
export function runUninstall({ purge = false } = {}) {
|
|
26
|
+
const components_removed = [];
|
|
27
|
+
const errors = [];
|
|
28
|
+
|
|
29
|
+
// 1. Plugin directories under ~/.openclaw/extensions/
|
|
30
|
+
const pluginDirs = [
|
|
31
|
+
{ id: 'robot-resources-router', label: 'router_plugin_dir' },
|
|
32
|
+
{ id: 'robot-resources-scraper-oc-plugin', label: 'scraper_plugin_dir' },
|
|
33
|
+
];
|
|
34
|
+
for (const { id, label } of pluginDirs) {
|
|
35
|
+
const path = join(homedir(), '.openclaw', 'extensions', id);
|
|
36
|
+
if (!existsSync(path)) continue;
|
|
37
|
+
try {
|
|
38
|
+
rmSync(path, { recursive: true, force: true });
|
|
39
|
+
components_removed.push(label);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
errors.push({ component: label, message: err.message });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 2. openclaw.json — strip our entries from plugins.entries, plugins.allow,
|
|
46
|
+
// and mcp.servers. Leave everything else (other plugins, user config) alone.
|
|
47
|
+
// Idempotent: if openclaw.json is missing or malformed, skip silently —
|
|
48
|
+
// that's the right behavior for "cleanup what you can find."
|
|
49
|
+
const ocConfigPath = join(homedir(), '.openclaw', 'openclaw.json');
|
|
50
|
+
if (existsSync(ocConfigPath)) {
|
|
51
|
+
try {
|
|
52
|
+
const config = JSON.parse(stripJson5(readFileSync(ocConfigPath, 'utf-8')));
|
|
53
|
+
let mutated = false;
|
|
54
|
+
|
|
55
|
+
if (config?.plugins?.entries) {
|
|
56
|
+
for (const id of ['robot-resources-router', 'robot-resources-scraper-oc-plugin']) {
|
|
57
|
+
if (config.plugins.entries[id]) {
|
|
58
|
+
delete config.plugins.entries[id];
|
|
59
|
+
mutated = true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (Array.isArray(config?.plugins?.allow)) {
|
|
65
|
+
const before = config.plugins.allow.length;
|
|
66
|
+
config.plugins.allow = config.plugins.allow.filter(
|
|
67
|
+
(id) => id !== 'robot-resources-router' && id !== 'robot-resources-scraper-oc-plugin',
|
|
68
|
+
);
|
|
69
|
+
if (config.plugins.allow.length !== before) mutated = true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (config?.mcp?.servers?.['robot-resources-scraper']) {
|
|
73
|
+
delete config.mcp.servers['robot-resources-scraper'];
|
|
74
|
+
mutated = true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (mutated) {
|
|
78
|
+
writeFileSync(ocConfigPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
79
|
+
components_removed.push('openclaw_config_entries');
|
|
80
|
+
}
|
|
81
|
+
} catch (err) {
|
|
82
|
+
errors.push({ component: 'openclaw_config_entries', message: err.message });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 3. Optionally wipe ~/.robot-resources/config.json (and any siblings)
|
|
87
|
+
if (purge) {
|
|
88
|
+
const rrDir = join(homedir(), '.robot-resources');
|
|
89
|
+
if (existsSync(rrDir)) {
|
|
90
|
+
try {
|
|
91
|
+
rmSync(rrDir, { recursive: true, force: true });
|
|
92
|
+
components_removed.push('rr_config_dir');
|
|
93
|
+
} catch (err) {
|
|
94
|
+
errors.push({ component: 'rr_config_dir', message: err.message });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return { components_removed, errors };
|
|
100
|
+
}
|
package/lib/wizard.js
CHANGED
|
@@ -8,6 +8,7 @@ import { configureToolRouting, registerScraperMcp, restartOpenClawGateway } from
|
|
|
8
8
|
import { checkHealth } from './health-report.js';
|
|
9
9
|
import { header, step, success, warn, error, info, blank, summary } from './ui.js';
|
|
10
10
|
import { runNonOcWizard } from './non-oc-wizard.js';
|
|
11
|
+
import { runUninstall } from './uninstall.js';
|
|
11
12
|
|
|
12
13
|
// Stamped onto every CLI telemetry payload so we can tell which `robot-resources`
|
|
13
14
|
// version a user actually ran. Without this, npx-cached old installers look
|
|
@@ -398,3 +399,71 @@ export async function runWizard({ nonInteractive = false, target = null } = {})
|
|
|
398
399
|
}
|
|
399
400
|
}
|
|
400
401
|
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Uninstall counterpart to runWizard. Removes the OC plugin install side
|
|
405
|
+
* (router + scraper plugin dirs, openclaw.json entries) via uninstall.js.
|
|
406
|
+
*
|
|
407
|
+
* config.json (and its api_key) is preserved by default so a later re-install
|
|
408
|
+
* keeps the same identity. `--purge` wipes ~/.robot-resources/ as well.
|
|
409
|
+
*
|
|
410
|
+
* Telemetry: emits `wizard_uninstalled` with the list of components actually
|
|
411
|
+
* removed plus any per-component errors. Fire-and-forget — never block the
|
|
412
|
+
* uninstall on telemetry latency or failure.
|
|
413
|
+
*/
|
|
414
|
+
export async function runUninstallCommand({ purge = false } = {}) {
|
|
415
|
+
header();
|
|
416
|
+
step(purge ? 'Uninstalling Robot Resources (purge)...' : 'Uninstalling Robot Resources...');
|
|
417
|
+
|
|
418
|
+
const result = runUninstall({ purge });
|
|
419
|
+
|
|
420
|
+
blank();
|
|
421
|
+
if (result.components_removed.length === 0 && result.errors.length === 0) {
|
|
422
|
+
info('Nothing to remove — Robot Resources was not installed in this account.');
|
|
423
|
+
} else {
|
|
424
|
+
if (result.components_removed.length > 0) {
|
|
425
|
+
success(`Removed: ${result.components_removed.join(', ')}`);
|
|
426
|
+
}
|
|
427
|
+
for (const e of result.errors) {
|
|
428
|
+
warn(`${e.component}: ${e.message}`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Preserve the api_key (unless --purge) so re-running `npx robot-resources`
|
|
433
|
+
// doesn't issue a second key. Tell the user explicitly so they can purge if
|
|
434
|
+
// they really want a clean slate.
|
|
435
|
+
if (!purge) {
|
|
436
|
+
blank();
|
|
437
|
+
info('Kept ~/.robot-resources/config.json (your api_key + claim_url).');
|
|
438
|
+
info('Re-run with --purge to wipe it.');
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Best-effort telemetry — same shape as the rest of the CLI's calls.
|
|
442
|
+
try {
|
|
443
|
+
const config = readConfig();
|
|
444
|
+
if (config.api_key) {
|
|
445
|
+
const platformUrl = process.env.RR_PLATFORM_URL || 'https://api.robotresources.ai';
|
|
446
|
+
await fetch(`${platformUrl}/v1/telemetry`, {
|
|
447
|
+
method: 'POST',
|
|
448
|
+
headers: {
|
|
449
|
+
'Authorization': `Bearer ${config.api_key}`,
|
|
450
|
+
'Content-Type': 'application/json',
|
|
451
|
+
},
|
|
452
|
+
body: JSON.stringify({
|
|
453
|
+
product: 'cli',
|
|
454
|
+
event_type: 'wizard_uninstalled',
|
|
455
|
+
payload: {
|
|
456
|
+
cli_version: CLI_VERSION,
|
|
457
|
+
purge,
|
|
458
|
+
components_removed: result.components_removed,
|
|
459
|
+
error_count: result.errors.length,
|
|
460
|
+
platform: process.platform,
|
|
461
|
+
},
|
|
462
|
+
}),
|
|
463
|
+
signal: AbortSignal.timeout(5_000),
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
} catch {
|
|
467
|
+
// Non-fatal — telemetry must never block the uninstall path.
|
|
468
|
+
}
|
|
469
|
+
}
|