robot-resources 1.2.5 → 1.2.7
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/detect.js +64 -1
- package/lib/tool-config.js +59 -15
- package/lib/wizard.js +4 -1
- package/package.json +1 -1
package/lib/detect.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execSync, execFileSync } from 'node:child_process';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
|
|
@@ -80,6 +80,69 @@ export function isOpenClawInstalled() {
|
|
|
80
80
|
return existsSync(join(home, '.openclaw')) || existsSync(join(home, 'openclaw.json'));
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Check if the Robot Resources OpenClaw plugin is already installed.
|
|
85
|
+
*/
|
|
86
|
+
export function isOpenClawPluginInstalled() {
|
|
87
|
+
const home = homedir();
|
|
88
|
+
return existsSync(join(home, '.openclaw', 'extensions', 'robot-resources-router'));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Detect OpenClaw auth mode: 'subscription' (OAuth token) or 'apikey'.
|
|
93
|
+
*
|
|
94
|
+
* Subscription users authenticate via Anthropic OAuth tokens.
|
|
95
|
+
* Anthropic rejects these tokens from third-party clients, so the
|
|
96
|
+
* only viable routing path is the OpenClaw plugin (not HTTP proxy).
|
|
97
|
+
*
|
|
98
|
+
* Detection order:
|
|
99
|
+
* 1. ANTHROPIC_AUTH_TOKEN env var → subscription
|
|
100
|
+
* 2. openclaw.json → auth.type or providers.anthropic.authToken
|
|
101
|
+
* 3. providers.anthropic.apiKey → apikey
|
|
102
|
+
* 4. Default → apikey (conservative — proxy works fine)
|
|
103
|
+
*/
|
|
104
|
+
export function getOpenClawAuthMode() {
|
|
105
|
+
// Env var is the strongest signal
|
|
106
|
+
if (process.env.ANTHROPIC_AUTH_TOKEN) return 'subscription';
|
|
107
|
+
if (process.env.ANTHROPIC_API_KEY) return 'apikey';
|
|
108
|
+
|
|
109
|
+
// Try reading openclaw.json
|
|
110
|
+
const home = homedir();
|
|
111
|
+
const candidates = [
|
|
112
|
+
join(home, '.openclaw', 'openclaw.json'),
|
|
113
|
+
join(home, 'openclaw.json'),
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
for (const configPath of candidates) {
|
|
117
|
+
if (!existsSync(configPath)) continue;
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const raw = readFileSync(configPath, 'utf-8');
|
|
121
|
+
// Strip JSON5 features inline (comments + trailing commas)
|
|
122
|
+
const clean = raw
|
|
123
|
+
.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\/\/.*$|\/\*[\s\S]*?\*\//gm,
|
|
124
|
+
(m) => (m.startsWith('"') || m.startsWith("'") ? m : ''))
|
|
125
|
+
.replace(/,\s*([\]}])/g, '$1');
|
|
126
|
+
const config = JSON.parse(clean);
|
|
127
|
+
|
|
128
|
+
// Check explicit auth type
|
|
129
|
+
if (config.auth?.type === 'oauth' || config.auth?.type === 'subscription') {
|
|
130
|
+
return 'subscription';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Check for authToken in providers
|
|
134
|
+
const anthropic = config.models?.providers?.anthropic
|
|
135
|
+
|| config.providers?.anthropic;
|
|
136
|
+
if (anthropic?.authToken) return 'subscription';
|
|
137
|
+
if (anthropic?.apiKey) return 'apikey';
|
|
138
|
+
} catch {
|
|
139
|
+
// Config unreadable — fall through
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return 'apikey';
|
|
144
|
+
}
|
|
145
|
+
|
|
83
146
|
/**
|
|
84
147
|
* Check if the router service is already registered.
|
|
85
148
|
*/
|
package/lib/tool-config.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { isOpenClawInstalled, isOpenClawPluginInstalled, getOpenClawAuthMode } from './detect.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Strip JSON5 features (comments + trailing commas) to produce valid JSON.
|
|
@@ -18,23 +19,66 @@ function stripJson5(text) {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
|
-
*
|
|
22
|
+
* Configure OpenClaw to route through Robot Resources Router.
|
|
22
23
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* Installs the @robot-resources/openclaw-plugin via OpenClaw's
|
|
25
|
+
* native plugin system. The plugin uses before_model_resolve to
|
|
26
|
+
* override the provider — survives gateway restarts because it
|
|
27
|
+
* lives in ~/.openclaw/extensions/, not in openclaw.json.
|
|
28
|
+
*
|
|
29
|
+
* Auth mode detection:
|
|
30
|
+
* - subscription (OAuth token): Plugin is REQUIRED. Anthropic rejects
|
|
31
|
+
* OAuth tokens from third-party clients, so HTTP proxy won't work.
|
|
32
|
+
* - apikey: Plugin is preferred (survives restarts) but proxy also works.
|
|
27
33
|
*/
|
|
28
34
|
function configureOpenClaw() {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
'OpenClaw
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
35
|
+
const authMode = getOpenClawAuthMode();
|
|
36
|
+
|
|
37
|
+
if (isOpenClawPluginInstalled()) {
|
|
38
|
+
return {
|
|
39
|
+
name: 'OpenClaw',
|
|
40
|
+
action: 'already_configured',
|
|
41
|
+
authMode,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
execFileSync('openclaw', ['plugins', 'install', '@robot-resources/openclaw-plugin'], {
|
|
47
|
+
stdio: 'ignore',
|
|
48
|
+
timeout: 30_000,
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
name: 'OpenClaw',
|
|
52
|
+
action: 'installed',
|
|
53
|
+
authMode,
|
|
54
|
+
note: authMode === 'subscription'
|
|
55
|
+
? 'Plugin required — subscription OAuth tokens are rejected by Anthropic when proxied via third-party clients.'
|
|
56
|
+
: undefined,
|
|
57
|
+
};
|
|
58
|
+
} catch {
|
|
59
|
+
// Plugin install failed — fall back to instructions
|
|
60
|
+
const instructions = [
|
|
61
|
+
'Could not auto-install plugin. Install manually:',
|
|
62
|
+
' openclaw plugins install @robot-resources/openclaw-plugin',
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
if (authMode === 'subscription') {
|
|
66
|
+
instructions.push(
|
|
67
|
+
'IMPORTANT: Subscription mode detected (OAuth token).',
|
|
68
|
+
'The plugin is required — HTTP proxy cannot forward OAuth tokens.',
|
|
69
|
+
'Anthropic rejects OAuth tokens from third-party clients.',
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
instructions.push('Docs: https://github.com/robot-resources/robot-resources');
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
name: 'OpenClaw',
|
|
77
|
+
action: 'instructions',
|
|
78
|
+
authMode,
|
|
79
|
+
instructions,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
38
82
|
}
|
|
39
83
|
|
|
40
84
|
/**
|
package/lib/wizard.js
CHANGED
|
@@ -185,8 +185,11 @@ export async function runWizard({ nonInteractive = false } = {}) {
|
|
|
185
185
|
success(`${r.name}: routing through localhost:3838`);
|
|
186
186
|
} else if (r.action === 'already_configured') {
|
|
187
187
|
success(`${r.name}: already configured`);
|
|
188
|
+
} else if (r.action === 'installed') {
|
|
189
|
+
success(`${r.name}: plugin installed`);
|
|
190
|
+
if (r.note) info(` ${r.note}`);
|
|
188
191
|
} else if (r.action === 'instructions') {
|
|
189
|
-
|
|
192
|
+
warn(`${r.name}: manual configuration needed:`);
|
|
190
193
|
for (const instruction of r.instructions) {
|
|
191
194
|
info(` ${instruction}`);
|
|
192
195
|
}
|