robot-resources 1.10.6 → 1.11.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/README.md +53 -51
- package/bin/setup.js +11 -2
- package/lib/non-oc-wizard.js +241 -0
- package/lib/tool-config.js +2 -2
- package/lib/wizard.js +28 -28
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,88 +1,90 @@
|
|
|
1
|
-
#
|
|
1
|
+
# robot-resources
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
Two products for any software that makes LLM API calls — chatbots, RAG pipelines, AI-powered apps, agent runtimes. Both run locally, both free.
|
|
6
|
-
|
|
7
|
-
**Router** — Intelligent LLM routing proxy. Classifies each prompt by task type, routes to the cheapest model that qualifies. 60-90% cost savings with zero quality loss.
|
|
8
|
-
|
|
9
|
-
**Scraper** — Token compression for web content. Fetches any URL, strips noise, returns clean markdown. Median 91% token reduction.
|
|
10
|
-
|
|
11
|
-
## Install
|
|
3
|
+
> One command to install Robot Resources tools for any agent stack.
|
|
12
4
|
|
|
13
5
|
```bash
|
|
14
6
|
npx robot-resources
|
|
15
7
|
```
|
|
16
8
|
|
|
17
|
-
|
|
9
|
+
The wizard detects what you're building and walks you through the right setup — OpenClaw plugin install, JS library path, Python SDK path, MCP config for Cursor / Claude Code, or just the docs URL.
|
|
18
10
|
|
|
19
|
-
## What
|
|
11
|
+
## What the wizard does
|
|
20
12
|
|
|
21
|
-
1. **
|
|
22
|
-
2. **
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
13
|
+
1. **Provisions an anonymous API key** via `POST /v1/auth/signup` (saved to `~/.robot-resources/config.json`). Optional — routing works without it; the key just lights up the dashboard at robotresources.ai/dashboard.
|
|
14
|
+
2. **Detects your stack**:
|
|
15
|
+
- OpenClaw installed (`~/.openclaw/`) → installs the router plugin (in-process HTTP server) + scraper OC plugin, patches `openclaw.json`, restarts the gateway. **No Python, no daemon, no system service.**
|
|
16
|
+
- Non-OC + cwd has `package.json` with LangChain/LangGraph/Mastra → preselects "JS/TS agent."
|
|
17
|
+
- Non-OC + cwd has `requirements.txt` / `pyproject.toml` → preselects "Python agent."
|
|
18
|
+
- Non-OC + Cursor or Claude Code installed → preselects "MCP tool."
|
|
19
|
+
3. **Runs the chosen path**:
|
|
20
|
+
- **JS/TS agent** → prints `npm install @robot-resources/router` + `import { routePrompt }` example
|
|
21
|
+
- **Python agent** → prints `pip install robot-resources` + `from robot_resources.router import route` example, plus an httpx fallback if you'd rather skip the SDK
|
|
22
|
+
- **Cursor / Claude Code** → writes the scraper MCP config into `~/.cursor/mcp.json` / `~/.claude/settings.json`
|
|
23
|
+
- **Docs** → prints the URL + exits
|
|
24
|
+
- **Install OpenClaw first** → redirect message + exits
|
|
26
25
|
|
|
27
|
-
##
|
|
26
|
+
## Flags
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
```
|
|
29
|
+
--for=<target> langchain | python | cursor | claude-code | docs
|
|
30
|
+
Skip the prompt and run that path directly.
|
|
31
|
+
Required for non-TTY contexts (CI, piped, etc.)
|
|
32
|
+
--non-interactive Treat as CI run regardless of TTY state
|
|
33
|
+
--yes / -y Same as --non-interactive
|
|
34
|
+
```
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
Without flags in a non-TTY context, the wizard prints the `--for=` hint and exits cleanly — never blocks waiting for stdin.
|
|
38
37
|
|
|
39
|
-
|
|
38
|
+
## Pre-set the API key
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
- Content-aware token estimation
|
|
43
|
-
- 3-tier fetch: fast, stealth (TLS fingerprint), render (headless browser)
|
|
44
|
-
- Multi-page BFS crawl with robots.txt compliance
|
|
45
|
-
- Median 91% token reduction per page
|
|
40
|
+
For fleets or CI:
|
|
46
41
|
|
|
47
|
-
|
|
42
|
+
```bash
|
|
43
|
+
export RR_API_KEY=rr_live_... # skip signup, use this key
|
|
44
|
+
npx robot-resources --for=cursor # or whatever path applies
|
|
45
|
+
```
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|-------|-------------|--------|
|
|
51
|
-
| **OpenClaw** | Plugin (auto-install) | Verified |
|
|
52
|
-
| **Claude Code** | MCP server | Verified |
|
|
53
|
-
| **Any OpenAI client** | HTTP proxy (localhost:3838) | Compatible |
|
|
54
|
-
| **Cursor** | MCP server | Compatible |
|
|
55
|
-
| **Windsurf** | MCP server | Compatible |
|
|
47
|
+
## Five paths, one wizard
|
|
56
48
|
|
|
57
|
-
|
|
49
|
+
| Path | What you get | Where |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| OpenClaw plugin | In-process router inside the OC gateway. Auto-routes Anthropic calls to the cheapest capable model. | `~/.openclaw/extensions/robot-resources-router/` |
|
|
52
|
+
| JS/TS agent | `@robot-resources/router/routing` — pure ESM, zero deps, offline keyword classifier. | npm |
|
|
53
|
+
| Python agent | `robot-resources` (singular) — thin httpx client over `/v1/route`. | PyPI |
|
|
54
|
+
| HTTP API | Any language with curl/fetch. Authed by API key. | `POST https://api.robotresources.ai/v1/route` |
|
|
55
|
+
| Cursor / Claude Code MCP | Scraper MCP wired into your tool's config (web fetches → 91% smaller markdown). | `~/.cursor/mcp.json` or `~/.claude/settings.json` |
|
|
58
56
|
|
|
59
|
-
|
|
60
|
-
npx -y @robot-resources/scraper-mcp # Scraper compression
|
|
61
|
-
```
|
|
57
|
+
Full integration docs: https://robotresources.ai/docs
|
|
62
58
|
|
|
63
|
-
##
|
|
59
|
+
## Architecture (post-PR-2.5)
|
|
64
60
|
|
|
65
|
-
|
|
61
|
+
The router used to be a Python daemon on `localhost:3838`. **Not anymore.** It now runs in-process inside whichever surface consumes it:
|
|
66
62
|
|
|
67
|
-
|
|
63
|
+
- **OpenClaw** — the plugin's `register()` starts an HTTP server on `127.0.0.1:18790` inside OC's node process. Lifetime tied to OC. Zero daemon to keep alive.
|
|
64
|
+
- **JS agents** — call `routePrompt()` directly. No HTTP at all. Pure function.
|
|
65
|
+
- **Python / curl** — call `POST /v1/route` on `api.robotresources.ai`. Server-side classifier on Cloudflare Workers.
|
|
68
66
|
|
|
69
|
-
|
|
67
|
+
User provider keys never leave the user's machine. The platform never receives, stores, or transmits them.
|
|
70
68
|
|
|
71
69
|
## Telemetry
|
|
72
70
|
|
|
73
|
-
Anonymous
|
|
71
|
+
Anonymous, fire-and-forget, opt-in via the wizard's API-key provisioning. Events: `wizard_started`, `wizard_path_chosen`, `install_complete`, `route_completed`, `route_via_api`, `route_via_lib`. No personal data, no request content, no provider keys.
|
|
74
72
|
|
|
75
|
-
|
|
73
|
+
## Pricing
|
|
74
|
+
|
|
75
|
+
Free. Unlimited. Your API keys never leave your machine.
|
|
76
76
|
|
|
77
77
|
## Links
|
|
78
78
|
|
|
79
79
|
- Website: https://robotresources.ai
|
|
80
|
+
- Docs: https://robotresources.ai/docs
|
|
80
81
|
- Dashboard: https://robotresources.ai/dashboard
|
|
81
|
-
-
|
|
82
|
+
- HTTP API: `POST https://api.robotresources.ai/v1/route`
|
|
82
83
|
- npm: https://www.npmjs.com/package/robot-resources
|
|
83
84
|
- GitHub: https://github.com/robot-resources/packages
|
|
84
85
|
- Discord: https://robotresources.ai/discord
|
|
85
86
|
- Contact: agent@robotresources.ai
|
|
87
|
+
- Agent docs: https://robotresources.ai/llms.txt
|
|
86
88
|
|
|
87
89
|
## License
|
|
88
90
|
|
package/bin/setup.js
CHANGED
|
@@ -3,9 +3,18 @@
|
|
|
3
3
|
import { runWizard } from '../lib/wizard.js';
|
|
4
4
|
|
|
5
5
|
const args = process.argv.slice(2);
|
|
6
|
-
const
|
|
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;
|
|
7
10
|
|
|
8
|
-
|
|
11
|
+
// Treat piped/CI runs (no TTY on stdin OR stdout) as non-interactive so the
|
|
12
|
+
// wizard never blocks on a prompt that can't be answered. The interactive
|
|
13
|
+
// menu is only opened when both stdin and stdout are real terminals.
|
|
14
|
+
const hasTty = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
15
|
+
const nonInteractive = explicitNonInteractive || !hasTty;
|
|
16
|
+
|
|
17
|
+
runWizard({ nonInteractive, target }).catch((err) => {
|
|
9
18
|
console.error(`\n ✗ Setup failed: ${err.message}\n`);
|
|
10
19
|
process.exit(1);
|
|
11
20
|
});
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { select } from '@inquirer/prompts';
|
|
4
|
+
import { isClaudeCodeInstalled, isCursorInstalled } from './detect.js';
|
|
5
|
+
import { configureClaudeCode, configureCursor } from './tool-config.js';
|
|
6
|
+
import { header, info, success, warn, blank } from './ui.js';
|
|
7
|
+
import { readConfig } from './config.mjs';
|
|
8
|
+
|
|
9
|
+
const PLATFORM_URL = process.env.RR_PLATFORM_URL || 'https://api.robotresources.ai';
|
|
10
|
+
|
|
11
|
+
const PATH_LABELS = {
|
|
12
|
+
js: 'JS/TS agent (LangChain, LangGraph, Mastra, etc.)',
|
|
13
|
+
python: 'Python agent (LangChain, LlamaIndex, CrewAI, etc.)',
|
|
14
|
+
mcp: 'Cursor / Claude Code / other MCP tool',
|
|
15
|
+
docs: "Just point me at docs, I'll integrate manually",
|
|
16
|
+
'install-oc': 'Install OpenClaw first — exit',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const VALID_TARGETS = new Set(Object.keys(PATH_LABELS).concat(['langchain', 'claude-code']));
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Inspect cwd to guess what the user is building. Returns one of the path
|
|
23
|
+
* keys, or null if we can't tell. Order matters: detect-by-file beats
|
|
24
|
+
* detect-by-installed-tool, since cwd evidence is stronger than "the user
|
|
25
|
+
* has Cursor installed somewhere on this machine."
|
|
26
|
+
*/
|
|
27
|
+
export function detectDefaultPath(cwd = process.cwd()) {
|
|
28
|
+
const pkgPath = join(cwd, 'package.json');
|
|
29
|
+
if (existsSync(pkgPath)) {
|
|
30
|
+
try {
|
|
31
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
32
|
+
const allDeps = {
|
|
33
|
+
...(pkg.dependencies ?? {}),
|
|
34
|
+
...(pkg.devDependencies ?? {}),
|
|
35
|
+
};
|
|
36
|
+
const jsAgentMarkers = ['langchain', '@langchain/core', '@langchain/langgraph', '@mastra/core', 'crewai-js', 'llamaindex'];
|
|
37
|
+
if (jsAgentMarkers.some((m) => Object.prototype.hasOwnProperty.call(allDeps, m))) {
|
|
38
|
+
return 'js';
|
|
39
|
+
}
|
|
40
|
+
// Generic JS project still defaults to JS (cheaper than asking).
|
|
41
|
+
return 'js';
|
|
42
|
+
} catch {
|
|
43
|
+
// fall through
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (existsSync(join(cwd, 'requirements.txt')) || existsSync(join(cwd, 'pyproject.toml'))) {
|
|
48
|
+
return 'python';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (isCursorInstalled() || isClaudeCodeInstalled()) {
|
|
52
|
+
return 'mcp';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function normalizeTarget(target) {
|
|
59
|
+
if (!target) return null;
|
|
60
|
+
const t = String(target).toLowerCase();
|
|
61
|
+
if (!VALID_TARGETS.has(t)) return null;
|
|
62
|
+
// Aliases — friendly synonyms map to canonical path keys.
|
|
63
|
+
if (t === 'langchain') return 'js';
|
|
64
|
+
if (t === 'claude-code') return 'mcp';
|
|
65
|
+
return t;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function emitPathChosen(path) {
|
|
69
|
+
const config = readConfig();
|
|
70
|
+
if (!config.api_key) return; // wizard didn't get to provision; can't authenticate
|
|
71
|
+
try {
|
|
72
|
+
await fetch(`${PLATFORM_URL}/v1/telemetry`, {
|
|
73
|
+
method: 'POST',
|
|
74
|
+
headers: {
|
|
75
|
+
Authorization: `Bearer ${config.api_key}`,
|
|
76
|
+
'Content-Type': 'application/json',
|
|
77
|
+
},
|
|
78
|
+
body: JSON.stringify({
|
|
79
|
+
product: 'cli',
|
|
80
|
+
event_type: 'wizard_path_chosen',
|
|
81
|
+
payload: { path, platform: process.platform },
|
|
82
|
+
}),
|
|
83
|
+
signal: AbortSignal.timeout(5_000),
|
|
84
|
+
});
|
|
85
|
+
} catch {
|
|
86
|
+
// Best-effort — never let telemetry break the install path.
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function showJsPath() {
|
|
91
|
+
blank();
|
|
92
|
+
success('JS/TS integration');
|
|
93
|
+
blank();
|
|
94
|
+
info('Install:');
|
|
95
|
+
info(' npm install @robot-resources/router');
|
|
96
|
+
blank();
|
|
97
|
+
info('Use:');
|
|
98
|
+
info(' import { routePrompt } from \'@robot-resources/router/routing\';');
|
|
99
|
+
info(' const decision = routePrompt(\'write a python function\');');
|
|
100
|
+
info(' console.log(decision.selected_model); // e.g. \'claude-haiku-4-5\'');
|
|
101
|
+
blank();
|
|
102
|
+
info('Full docs: https://robotresources.ai/docs/langchain');
|
|
103
|
+
blank();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function showPythonPath() {
|
|
107
|
+
blank();
|
|
108
|
+
success('Python integration');
|
|
109
|
+
blank();
|
|
110
|
+
info('Install:');
|
|
111
|
+
info(' pip install robot-resources');
|
|
112
|
+
blank();
|
|
113
|
+
info('Use:');
|
|
114
|
+
info(' from robot_resources.router import route');
|
|
115
|
+
info(' decision = route(\'write a python function\')');
|
|
116
|
+
info(' print(decision[\'selected_model\']) # e.g. \'claude-haiku-4-5\'');
|
|
117
|
+
blank();
|
|
118
|
+
info('Prefer no SDK? POST directly to https://api.robotresources.ai/v1/route');
|
|
119
|
+
info('with httpx / requests / any HTTP client. See docs.');
|
|
120
|
+
blank();
|
|
121
|
+
info('Full docs: https://robotresources.ai/docs/crewai');
|
|
122
|
+
blank();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function showMcpPath() {
|
|
126
|
+
blank();
|
|
127
|
+
success('MCP tool integration');
|
|
128
|
+
blank();
|
|
129
|
+
let cursorOk = false;
|
|
130
|
+
let claudeOk = false;
|
|
131
|
+
if (isCursorInstalled()) {
|
|
132
|
+
try {
|
|
133
|
+
const result = configureCursor();
|
|
134
|
+
cursorOk = result?.action === 'configured' || result?.action === 'already_configured';
|
|
135
|
+
info(`Cursor: ${cursorOk ? 'configured' : 'see manual instructions below'}`);
|
|
136
|
+
} catch {
|
|
137
|
+
warn('Cursor: failed to write ~/.cursor/mcp.json automatically');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (isClaudeCodeInstalled()) {
|
|
141
|
+
try {
|
|
142
|
+
const result = configureClaudeCode();
|
|
143
|
+
claudeOk = result?.action === 'configured' || result?.action === 'already_configured';
|
|
144
|
+
info(`Claude Code: ${claudeOk ? 'configured' : 'see manual instructions below'}`);
|
|
145
|
+
} catch {
|
|
146
|
+
warn('Claude Code: failed to write ~/.claude/settings.json automatically');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (!cursorOk && !claudeOk) {
|
|
150
|
+
info('We did not detect Cursor or Claude Code on this machine.');
|
|
151
|
+
info('Manual setup: https://robotresources.ai/docs/cursor-mcp');
|
|
152
|
+
}
|
|
153
|
+
blank();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function showDocsPath() {
|
|
157
|
+
blank();
|
|
158
|
+
success('Docs');
|
|
159
|
+
blank();
|
|
160
|
+
info('Integration guides: https://robotresources.ai/docs');
|
|
161
|
+
info('HTTP API: https://robotresources.ai/docs/http-api');
|
|
162
|
+
info('GitHub: https://github.com/robot-resources/packages');
|
|
163
|
+
blank();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function showInstallOcPath() {
|
|
167
|
+
blank();
|
|
168
|
+
info('OpenClaw is the easiest way to use Robot Resources.');
|
|
169
|
+
info('Install OpenClaw first (https://openclaw.dev), then re-run:');
|
|
170
|
+
info(' npx robot-resources');
|
|
171
|
+
blank();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function runPath(path) {
|
|
175
|
+
switch (path) {
|
|
176
|
+
case 'js': showJsPath(); break;
|
|
177
|
+
case 'python': showPythonPath(); break;
|
|
178
|
+
case 'mcp': showMcpPath(); break;
|
|
179
|
+
case 'docs': showDocsPath(); break;
|
|
180
|
+
case 'install-oc': showInstallOcPath(); break;
|
|
181
|
+
default: showInstallOcPath(); break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Runs the non-OC wizard. Three modes:
|
|
187
|
+
* - target supplied (--for=<target>): run that path directly, no prompt
|
|
188
|
+
* - non-interactive AND no target: print hint with --for= options and exit
|
|
189
|
+
* - interactive: 5-option menu via @inquirer/prompts.select
|
|
190
|
+
*/
|
|
191
|
+
export async function runNonOcWizard({ nonInteractive = false, target = null } = {}) {
|
|
192
|
+
const normalized = normalizeTarget(target);
|
|
193
|
+
|
|
194
|
+
if (normalized) {
|
|
195
|
+
runPath(normalized);
|
|
196
|
+
await emitPathChosen(normalized);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (nonInteractive) {
|
|
201
|
+
info('Robot Resources requires OpenClaw, which we did not detect on this machine.');
|
|
202
|
+
info('To bypass this prompt in CI / non-TTY contexts, re-run with --for=<target>:');
|
|
203
|
+
info(' npx robot-resources --for=langchain # JS/TS agent');
|
|
204
|
+
info(' npx robot-resources --for=python # Python agent');
|
|
205
|
+
info(' npx robot-resources --for=cursor # Cursor MCP config');
|
|
206
|
+
info(' npx robot-resources --for=claude-code # Claude Code MCP config');
|
|
207
|
+
info(' npx robot-resources --for=docs # docs URL');
|
|
208
|
+
blank();
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Interactive menu.
|
|
213
|
+
header();
|
|
214
|
+
info('Robot Resources requires OpenClaw, which we did not detect on this machine.');
|
|
215
|
+
info('What are you building? Pick the closest match — we\'ll show the install steps.');
|
|
216
|
+
blank();
|
|
217
|
+
|
|
218
|
+
const defaultPath = detectDefaultPath() ?? 'js';
|
|
219
|
+
|
|
220
|
+
let chosen;
|
|
221
|
+
try {
|
|
222
|
+
chosen = await select({
|
|
223
|
+
message: 'What are you building?',
|
|
224
|
+
default: defaultPath,
|
|
225
|
+
choices: [
|
|
226
|
+
{ name: PATH_LABELS.js, value: 'js' },
|
|
227
|
+
{ name: PATH_LABELS.python, value: 'python' },
|
|
228
|
+
{ name: PATH_LABELS.mcp, value: 'mcp' },
|
|
229
|
+
{ name: PATH_LABELS.docs, value: 'docs' },
|
|
230
|
+
{ name: PATH_LABELS['install-oc'], value: 'install-oc' },
|
|
231
|
+
],
|
|
232
|
+
});
|
|
233
|
+
} catch (err) {
|
|
234
|
+
// User hit Ctrl-C or terminal closed — exit cleanly.
|
|
235
|
+
if (err && (err.name === 'ExitPromptError' || err.code === 'ABORT_ERR')) return;
|
|
236
|
+
throw err;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
runPath(chosen);
|
|
240
|
+
await emitPathChosen(chosen);
|
|
241
|
+
}
|
package/lib/tool-config.js
CHANGED
|
@@ -301,7 +301,7 @@ function configureOpenClaw() {
|
|
|
301
301
|
);
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
-
instructions.push('Docs: https://github.com/robot-resources/
|
|
304
|
+
instructions.push('Docs: https://github.com/robot-resources/packages');
|
|
305
305
|
|
|
306
306
|
return {
|
|
307
307
|
name: 'OpenClaw',
|
|
@@ -339,7 +339,7 @@ function printManualInstructions() {
|
|
|
339
339
|
' # OpenAI(base_url="http://localhost:3838/v1")',
|
|
340
340
|
' # model = "gemini-2.5-flash"',
|
|
341
341
|
'',
|
|
342
|
-
'Docs: https://github.com/robot-resources/
|
|
342
|
+
'Docs: https://github.com/robot-resources/packages',
|
|
343
343
|
],
|
|
344
344
|
};
|
|
345
345
|
}
|
package/lib/wizard.js
CHANGED
|
@@ -7,6 +7,7 @@ import { getOrCreateMachineId } from './machine-id.js';
|
|
|
7
7
|
import { configureToolRouting, registerScraperMcp, restartOpenClawGateway } from './tool-config.js';
|
|
8
8
|
import { checkHealth } from './health-report.js';
|
|
9
9
|
import { header, step, success, warn, error, info, blank, summary } from './ui.js';
|
|
10
|
+
import { runNonOcWizard } from './non-oc-wizard.js';
|
|
10
11
|
|
|
11
12
|
// Stamped onto every CLI telemetry payload so we can tell which `robot-resources`
|
|
12
13
|
// version a user actually ran. Without this, npx-cached old installers look
|
|
@@ -41,33 +42,20 @@ const CLI_VERSION = (() => {
|
|
|
41
42
|
*
|
|
42
43
|
* No Python, no venv, no systemd, no port probe.
|
|
43
44
|
*/
|
|
44
|
-
export async function runWizard({ nonInteractive = false } = {}) {
|
|
45
|
+
export async function runWizard({ nonInteractive = false, target = null } = {}) {
|
|
45
46
|
header();
|
|
46
47
|
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
// side effects against a machine that can't actually use the product.
|
|
52
|
-
// Non-interactive callers (CI, agents, scripts that pre-set RR_API_KEY)
|
|
53
|
-
// bypass: they explicitly chose to run the wizard.
|
|
54
|
-
if (!isOpenClawInstalled() && !nonInteractive) {
|
|
55
|
-
info('Robot Resources requires OpenClaw, which we did not detect on this machine.');
|
|
56
|
-
info('Install OpenClaw first (https://openclaw.dev), then re-run:');
|
|
57
|
-
info(' npx robot-resources');
|
|
58
|
-
blank();
|
|
59
|
-
info('If you are integrating Robot Resources into a non-OC agent, see PR 7 docs');
|
|
60
|
-
info('(coming soon — https://robotresources.ai/docs/integrations).');
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
48
|
+
// Detect OC once up front. Used both to branch into the non-OC wizard and
|
|
49
|
+
// to tag the wizard_started payload, so the funnel can be segmented OC vs
|
|
50
|
+
// non-OC without a second event type.
|
|
51
|
+
const openclawDetected = isOpenClawInstalled();
|
|
64
52
|
const wizardStartMs = Date.now();
|
|
65
53
|
|
|
66
54
|
const results = {
|
|
67
55
|
auth: false,
|
|
68
56
|
authMethod: null, // 'config' | 'apikey' | 'auto'
|
|
69
57
|
pluginInstalled: false,
|
|
70
|
-
openclawDetected
|
|
58
|
+
openclawDetected,
|
|
71
59
|
openclawConfigPatched: false,
|
|
72
60
|
scraperMcpRegistered: false,
|
|
73
61
|
scraper: false,
|
|
@@ -75,9 +63,11 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
75
63
|
|
|
76
64
|
// ── Step 0: Provision API key (before anything else) ────────────────────
|
|
77
65
|
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
66
|
+
// Runs for BOTH the OC and non-OC paths. Provisioning before the non-OC
|
|
67
|
+
// hand-off closes the funnel blind spot where every non-OpenClaw install
|
|
68
|
+
// was invisible to telemetry (no api_keys row, no wizard_started, no
|
|
69
|
+
// agent_signup_meta). If the session dies later, telemetry still works
|
|
70
|
+
// for all tools. Single fetch() with 10s timeout — no prompts, no browser.
|
|
81
71
|
|
|
82
72
|
{
|
|
83
73
|
const config = readConfig();
|
|
@@ -121,10 +111,11 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
121
111
|
|
|
122
112
|
// ── Funnel marker: wizard_started ───────────────────────────────────────
|
|
123
113
|
//
|
|
124
|
-
// Sent immediately after auth
|
|
125
|
-
// point even if
|
|
126
|
-
//
|
|
127
|
-
//
|
|
114
|
+
// Sent immediately after auth, before either path branches, so we have
|
|
115
|
+
// proof the wizard reached this point even if a later step crashes. Pairs
|
|
116
|
+
// with install_complete (OC path) or wizard_path_chosen (non-OC path) to
|
|
117
|
+
// give us a "started → done" funnel. The openclaw_detected field lets us
|
|
118
|
+
// segment OC vs non-OC funnels without a second event type.
|
|
128
119
|
//
|
|
129
120
|
// Timeout asymmetry vs install_complete (5s, no retry vs 10s × 2 attempts):
|
|
130
121
|
// wizard_started is a best-effort funnel marker — losing it just means we
|
|
@@ -149,6 +140,7 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
149
140
|
cli_version: CLI_VERSION,
|
|
150
141
|
auth_method: results.authMethod,
|
|
151
142
|
non_interactive: nonInteractive,
|
|
143
|
+
openclaw_detected: openclawDetected,
|
|
152
144
|
},
|
|
153
145
|
}),
|
|
154
146
|
signal: AbortSignal.timeout(5_000),
|
|
@@ -158,6 +150,15 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
158
150
|
}
|
|
159
151
|
}
|
|
160
152
|
|
|
153
|
+
// Non-OC branch. Hands off to the multi-agent compatibility wizard which
|
|
154
|
+
// routes the user to the right install path (npm install / pip install /
|
|
155
|
+
// MCP config / docs / install-OC). The non-OC wizard's wizard_path_chosen
|
|
156
|
+
// telemetry now fires too, since Step 0 above provisioned an api_key.
|
|
157
|
+
if (!openclawDetected) {
|
|
158
|
+
await runNonOcWizard({ nonInteractive, target });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
161
162
|
// ── Step 1: Tool Routing Configuration ──────────────────────────────────
|
|
162
163
|
//
|
|
163
164
|
// Installs the OC plugin (which is @robot-resources/router — the router
|
|
@@ -171,7 +172,6 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
171
172
|
const toolResults = configureToolRouting();
|
|
172
173
|
results.tools = toolResults;
|
|
173
174
|
|
|
174
|
-
results.openclawDetected = isOpenClawInstalled();
|
|
175
175
|
const ocResult = toolResults.find((r) => r.name === 'OpenClaw');
|
|
176
176
|
if (ocResult) {
|
|
177
177
|
results.pluginInstalled =
|
|
@@ -389,7 +389,7 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
389
389
|
// Telegram survives this restart. If the session dies here, the agent
|
|
390
390
|
// picks up on the next message with all tools loaded.
|
|
391
391
|
|
|
392
|
-
if (
|
|
392
|
+
if (openclawDetected && (results.tools?.some(r => r.action === 'installed') || scraperRegistered)) {
|
|
393
393
|
try {
|
|
394
394
|
await restartOpenClawGateway();
|
|
395
395
|
success('OpenClaw gateway restarted');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "robot-resources",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"description": "Robot Resources — AI agent tools. One command to install everything.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"README.md"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
+
"@inquirer/prompts": "^7.0.0",
|
|
20
21
|
"@robot-resources/router": "*",
|
|
21
22
|
"@robot-resources/scraper": "*"
|
|
22
23
|
},
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
},
|
|
46
47
|
"homepage": "https://robotresources.ai",
|
|
47
48
|
"bugs": {
|
|
48
|
-
"url": "https://github.com/robot-resources/
|
|
49
|
+
"url": "https://github.com/robot-resources/packages/issues"
|
|
49
50
|
},
|
|
50
51
|
"publishConfig": {
|
|
51
52
|
"access": "public"
|