robot-resources 1.12.3 → 1.13.0

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.
@@ -1,6 +1,7 @@
1
1
  import { writeShellLine, hasShellLine } from './shell-config.js';
2
2
  import { readConfig } from './config.mjs';
3
3
  import { detectNodeAgent } from './detect.js';
4
+ import { installRouterFiles } from './install-router-files.js';
4
5
 
5
6
  const PLATFORM_URL = process.env.RR_PLATFORM_URL || 'https://api.robotresources.ai';
6
7
 
@@ -9,16 +10,20 @@ const PLATFORM_URL = process.env.RR_PLATFORM_URL || 'https://api.robotresources.
9
10
  * the non-OC Node path.
10
11
  *
11
12
  * Steps:
12
- * 1. Append the marker block to detected rc files (zsh / bash / fish)
13
- * via shell-config.writeShellLine. Idempotent: re-running does nothing
14
- * if the block is already present.
15
- * 2. Emit `node_shim_installed` telemetry with the shell list, sdks
16
- * detected, dry-run flag, plus per-file errors.
13
+ * 1. Copy the bundled `@robot-resources/router` files to a stable absolute
14
+ * path under `~/.robot-resources/router/` (mirrors the OC plugin pattern
15
+ * at `~/.openclaw/extensions/`). Phase 8 fix: previously NODE_OPTIONS
16
+ * used the bare module name `@robot-resources/router/auto` which only
17
+ * resolved when the user was cd'd inside a project that had the
18
+ * package in its node_modules. From any other cwd, EVERY `node`
19
+ * command crashed with "Cannot find module".
20
+ * 2. Append the marker block to detected rc files (zsh / bash / fish)
21
+ * with the ABSOLUTE PATH to the copied auto.cjs.
22
+ * 3. Emit `node_shim_installed` telemetry.
17
23
  *
18
24
  * The user has to start a new shell (or `source` the file) for the
19
25
  * NODE_OPTIONS to take effect — we tell them this in the wizard's
20
- * post-install message. For Phase 3 we don't try to mutate the running
21
- * shell; that's a Phase 6 nice-to-have.
26
+ * post-install message.
22
27
  *
23
28
  * Windows: shell-config.writeShellLine returns no rc files on Windows
24
29
  * (we only support POSIX in P3). The wizard prints manual instructions
@@ -40,8 +45,8 @@ export async function installNodeShim({ cwd = process.cwd(), dryRun = false } =
40
45
  reason: 'windows_not_supported_yet',
41
46
  message:
42
47
  'Windows shell-config writing is not yet supported. Set ' +
43
- 'NODE_OPTIONS=--require @robot-resources/router/auto manually in your ' +
44
- 'system environment variables, or wait for Phase 6.',
48
+ 'NODE_OPTIONS to point at ~/.robot-resources/router/auto.cjs manually ' +
49
+ 'in your system environment variables, or wait for Phase 6.',
45
50
  };
46
51
  }
47
52
 
@@ -58,8 +63,29 @@ export async function installNodeShim({ cwd = process.cwd(), dryRun = false } =
58
63
  return { ok: true, message: 'Dry-run: would have written NODE_OPTIONS to shell rc.' };
59
64
  }
60
65
 
66
+ // Phase 8: copy router to an absolute path under ~/.robot-resources/router/
67
+ // before we wire the shell config. If the copy fails, we don't write a
68
+ // broken NODE_OPTIONS line.
69
+ let autoPath;
70
+ try {
71
+ autoPath = installRouterFiles();
72
+ } catch (err) {
73
+ await emit({
74
+ shell: 'unknown',
75
+ shell_config_path: null,
76
+ sdks_detected: sdks,
77
+ dry_run: false,
78
+ reason: 'router_copy_failed',
79
+ error_messages: [err.message],
80
+ });
81
+ return {
82
+ ok: false,
83
+ message: `Could not copy router files to ~/.robot-resources/router/: ${err.message}`,
84
+ };
85
+ }
86
+
61
87
  const alreadyInstalled = hasShellLine();
62
- const result = writeShellLine();
88
+ const result = writeShellLine({ autoPath });
63
89
 
64
90
  // Single shell value for the funnel even though we may have written to
65
91
  // multiple rc files. Pick the dominant one for telemetry.
@@ -74,6 +100,7 @@ export async function installNodeShim({ cwd = process.cwd(), dryRun = false } =
74
100
  files_written: result.written.length,
75
101
  files_with_errors: result.errors.length,
76
102
  error_messages: result.errors.map((e) => `${e.path}: ${e.message}`).slice(0, 3),
103
+ auto_path: autoPath,
77
104
  });
78
105
 
79
106
  if (alreadyInstalled && result.written.length === 0) {
@@ -0,0 +1,48 @@
1
+ import { existsSync, mkdirSync, copyFileSync, cpSync, rmSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join, dirname } from 'node:path';
4
+ import { createRequire } from 'node:module';
5
+
6
+ const require = createRequire(import.meta.url);
7
+
8
+ /**
9
+ * Copy `@robot-resources/router` to ~/.robot-resources/router/ and return
10
+ * the absolute path to its auto.cjs.
11
+ *
12
+ * Phase 8 fix. Mirrors `installPluginFiles()` in tool-config.js (the OC
13
+ * plugin path that's worked since Phase 0). The destination is a stable
14
+ * user-scoped location that survives npm/npx cache cleanups, so the
15
+ * NODE_OPTIONS line we write to shell rc doesn't break when caches expire.
16
+ *
17
+ * Files copied: auto.cjs, index.js, package.json, lib/ (recursive).
18
+ * Each call wipes lib/ first so files removed in newer router versions
19
+ * don't linger from a previous install.
20
+ *
21
+ * Why this is its own module: extracted from install-node-shim.js for
22
+ * testability — vitest can mock the whole module without us mocking
23
+ * node:fs / node:module manually in every install-shim test case.
24
+ */
25
+ export function installRouterFiles({ home = homedir() } = {}) {
26
+ const pkgPath = require.resolve('@robot-resources/router/package.json');
27
+ const pkgDir = dirname(pkgPath);
28
+ const targetDir = join(home, '.robot-resources', 'router');
29
+ mkdirSync(targetDir, { recursive: true });
30
+
31
+ for (const file of ['auto.cjs', 'index.js', 'package.json']) {
32
+ const src = join(pkgDir, file);
33
+ if (existsSync(src)) {
34
+ copyFileSync(src, join(targetDir, file));
35
+ }
36
+ }
37
+
38
+ // Refresh lib/ — wipe + recopy so we don't accumulate stale files across
39
+ // router upgrades. Same pattern as tool-config.js installPluginFiles().
40
+ const srcLib = join(pkgDir, 'lib');
41
+ const dstLib = join(targetDir, 'lib');
42
+ if (existsSync(srcLib)) {
43
+ rmSync(dstLib, { recursive: true, force: true });
44
+ cpSync(srcLib, dstLib, { recursive: true });
45
+ }
46
+
47
+ return join(targetDir, 'auto.cjs');
48
+ }
@@ -131,16 +131,14 @@ async function showPythonPath() {
131
131
  info(` Detected SDKs: ${result.sdks.join(', ')}`);
132
132
  }
133
133
  blank();
134
- info('Set RR_AUTOATTACH=1 in your shell, then run your Python agent.');
135
- info('Every anthropic.Anthropic() instance routes through Robot Resources.');
136
- info(' echo \'export RR_AUTOATTACH=1\' >> ~/.zshrc # or your shell rc');
134
+ info('Run your Python agent every anthropic / openai / google_generativeai');
135
+ info('SDK call routes through Robot Resources automatically.');
136
+ info('To opt out for a single command: RR_AUTOATTACH=0 python your-script.py');
137
137
  } else {
138
138
  warn(result.message);
139
139
  blank();
140
140
  info('Manual install (run inside your venv):');
141
141
  info(' pip install --upgrade robot-resources');
142
- info('Then set:');
143
- info(' export RR_AUTOATTACH=1');
144
142
  }
145
143
  blank();
146
144
  info('Docs: https://robotresources.ai/docs/crewai');
@@ -11,10 +11,19 @@ import { join } from 'node:path';
11
11
  * regex-matching against the user's actual shell content:
12
12
  *
13
13
  * # >>> robot-resources: NODE_OPTIONS auto-attach >>>
14
- * export NODE_OPTIONS="${NODE_OPTIONS:-} --require @robot-resources/router/auto"
14
+ * export NODE_OPTIONS="${NODE_OPTIONS:-} --require /Users/x/.robot-resources/router/auto.cjs"
15
15
  * # <<< robot-resources <<<
16
16
  *
17
- * Behavior decisions (see plan):
17
+ * Phase 8 fix: NODE_OPTIONS now uses an ABSOLUTE PATH to the auto.cjs the
18
+ * wizard copied to ~/.robot-resources/router/. The previous bare-module
19
+ * form `--require @robot-resources/router/auto` only resolved when the user
20
+ * was cd'd inside a project that had `@robot-resources/router` in its
21
+ * node_modules — and broke EVERY Node command from any other cwd with
22
+ * `Cannot find module`. Result: every wizard-success Node user pre-Phase-8
23
+ * had a NODE_OPTIONS line that crashed `node`/`npm`/etc. Symptom in
24
+ * Supabase: `node_shim_installed: 8` but `adapter_attached: 0`.
25
+ *
26
+ * Behavior decisions (preserved from Phase 3):
18
27
  * - If NODE_OPTIONS is already set with a different --require (rare; e.g.
19
28
  * dd-trace), append ours after theirs. Both load. The user keeps their
20
29
  * existing tooling. The shell expansion `${NODE_OPTIONS:-} ...` handles
@@ -27,12 +36,14 @@ import { join } from 'node:path';
27
36
  const MARK_BEGIN = '# >>> robot-resources: NODE_OPTIONS auto-attach >>>';
28
37
  const MARK_END = '# <<< robot-resources <<<';
29
38
 
30
- const POSIX_LINE =
31
- 'export NODE_OPTIONS="${NODE_OPTIONS:-} --require @robot-resources/router/auto"';
39
+ function buildPosixLine(autoPath) {
40
+ return `export NODE_OPTIONS="\${NODE_OPTIONS:-} --require ${autoPath}"`;
41
+ }
32
42
 
33
- // Fish has different syntax (no `export`, uses `set -x`). Detected separately.
34
- const FISH_LINE =
35
- 'set -x NODE_OPTIONS "$NODE_OPTIONS --require @robot-resources/router/auto"';
43
+ function buildFishLine(autoPath) {
44
+ // Fish has different syntax (no `export`, uses `set -x`).
45
+ return `set -x NODE_OPTIONS "$NODE_OPTIONS --require ${autoPath}"`;
46
+ }
36
47
 
37
48
  /**
38
49
  * Discover which rc files are present for this user. Returns a list of
@@ -73,7 +84,11 @@ export function hasShellLine(home = homedir()) {
73
84
  * others on one failure. Per-file errors are returned as warnings the
74
85
  * caller can surface.
75
86
  */
76
- export function writeShellLine(home = homedir()) {
87
+ export function writeShellLine({ autoPath, home = homedir() }) {
88
+ if (!autoPath) {
89
+ throw new Error('writeShellLine requires { autoPath } — absolute path to auto.cjs');
90
+ }
91
+
77
92
  const rcs = listShellRcFiles(home);
78
93
  const written = [];
79
94
  const errors = [];
@@ -97,7 +112,7 @@ export function writeShellLine(home = homedir()) {
97
112
  continue;
98
113
  }
99
114
 
100
- const line = rc.kind === 'fish' ? FISH_LINE : POSIX_LINE;
115
+ const line = rc.kind === 'fish' ? buildFishLine(autoPath) : buildPosixLine(autoPath);
101
116
  const block =
102
117
  (text && !text.endsWith('\n') ? '\n' : '') +
103
118
  '\n' + MARK_BEGIN + '\n' + line + '\n' + MARK_END + '\n';
@@ -165,4 +180,4 @@ function getMode(path) {
165
180
  }
166
181
 
167
182
  // Exported for tests + telemetry payloads.
168
- export { MARK_BEGIN, MARK_END, POSIX_LINE, FISH_LINE };
183
+ export { MARK_BEGIN, MARK_END, buildPosixLine, buildFishLine };
package/lib/uninstall.js CHANGED
@@ -100,6 +100,19 @@ export function runUninstall({ purge = false } = {}) {
100
100
  errors.push({ component: 'shell_config_node_options', message: err.message });
101
101
  }
102
102
 
103
+ // 3b. Copied router dir at ~/.robot-resources/router/ (Phase 8). The shell
104
+ // line points at this absolute path — once the line is gone, the
105
+ // copied files are dead weight. Remove them.
106
+ const routerDir = join(homedir(), '.robot-resources', 'router');
107
+ if (existsSync(routerDir)) {
108
+ try {
109
+ rmSync(routerDir, { recursive: true, force: true });
110
+ components_removed.push('node_shim_router_dir');
111
+ } catch (err) {
112
+ errors.push({ component: 'node_shim_router_dir', message: err.message });
113
+ }
114
+ }
115
+
103
116
  // 4. Python shim — `pip uninstall -y robot-resources` against the resolved
104
117
  // venv. Skip silently if no venv detected (the user may have installed
105
118
  // via the wizard but already deleted the venv themselves).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "robot-resources",
3
- "version": "1.12.3",
3
+ "version": "1.13.0",
4
4
  "description": "Robot Resources — AI agent tools. One command to install everything.",
5
5
  "type": "module",
6
6
  "bin": {