colana 1.0.0-beta.49 → 1.0.0-beta.50
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/package.json +1 -1
- package/server/personal-agent-routes.js +25 -5
- package/server/pty-manager.js +7 -19
package/package.json
CHANGED
|
@@ -157,15 +157,35 @@ export function registerPersonalAgentRoutes(app, { sensitiveLimiter }) {
|
|
|
157
157
|
const providerLabel = provider || null;
|
|
158
158
|
const envVar = provider ? PROVIDER_ENV_MAP[provider] : null;
|
|
159
159
|
|
|
160
|
-
// Check if the error body hints at provider-level auth failure
|
|
160
|
+
// Check if the error body hints at provider-level auth failure.
|
|
161
|
+
// OpenClaw gateway proxies upstream 401s as-is. The upstream error body
|
|
162
|
+
// varies by provider — some include "api_key", others just "unauthorized".
|
|
163
|
+
// We also probe /v1/models with our token: if that works, the gateway
|
|
164
|
+
// accepts our token and the 401 must be from the upstream provider.
|
|
161
165
|
const isProviderAuth = errText.includes('api_key') || errText.includes('API key')
|
|
162
|
-
|| errText.includes('invalid_api_key') || errText.includes('authentication_error')
|
|
166
|
+
|| errText.includes('invalid_api_key') || errText.includes('authentication_error')
|
|
167
|
+
|| errText.includes('"type":"unauthorized"') || errText.includes('"type": "unauthorized"');
|
|
168
|
+
|
|
169
|
+
// If provider-level detection didn't match, verify if gateway auth is actually fine
|
|
170
|
+
// by probing /v1/models (lightweight, always available)
|
|
171
|
+
let gatewayAuthOk = isProviderAuth;
|
|
172
|
+
if (!gatewayAuthOk) {
|
|
173
|
+
try {
|
|
174
|
+
const probeRes = await fetch(`http://127.0.0.1:${port}/v1/models`, {
|
|
175
|
+
headers: { 'Authorization': `Bearer ${authToken}` },
|
|
176
|
+
signal: AbortSignal.timeout(3000),
|
|
177
|
+
});
|
|
178
|
+
// If /v1/models returns 200, our gateway token is accepted —
|
|
179
|
+
// so the 401 on /v1/chat/completions is from the upstream provider
|
|
180
|
+
gatewayAuthOk = probeRes.ok;
|
|
181
|
+
} catch { /* probe failed — assume gateway token issue */ }
|
|
182
|
+
}
|
|
163
183
|
|
|
164
|
-
if (
|
|
184
|
+
if (gatewayAuthOk) {
|
|
165
185
|
return res.status(401).json({
|
|
166
186
|
error: providerLabel
|
|
167
187
|
? `Authentication failed — your ${providerLabel} API key may be missing or invalid.`
|
|
168
|
-
: 'Authentication failed — your API key may be missing or invalid.',
|
|
188
|
+
: 'Authentication failed — your model provider API key may be missing or invalid.',
|
|
169
189
|
code: 'GATEWAY_AUTH_FAILED',
|
|
170
190
|
fix: envVar
|
|
171
191
|
? `Add or update your ${envVar} in Settings > Personal AI Agents.`
|
|
@@ -174,7 +194,7 @@ export function registerPersonalAgentRoutes(app, { sensitiveLimiter }) {
|
|
|
174
194
|
});
|
|
175
195
|
}
|
|
176
196
|
|
|
177
|
-
//
|
|
197
|
+
// Gateway itself rejected our token — needs restart to resync
|
|
178
198
|
return res.status(401).json({
|
|
179
199
|
error: 'Gateway authentication failed — the auth token may be stale.',
|
|
180
200
|
code: 'GATEWAY_TOKEN_STALE',
|
package/server/pty-manager.js
CHANGED
|
@@ -317,30 +317,18 @@ async function ensureOpenClawConfigured() {
|
|
|
317
317
|
|
|
318
318
|
/**
|
|
319
319
|
* Verify that the running gateway accepts our bearer token.
|
|
320
|
-
* Uses
|
|
321
|
-
*
|
|
320
|
+
* Uses GET /v1/models — a lightweight endpoint that checks gateway-level
|
|
321
|
+
* auth without hitting any upstream provider (avoids false 401s from
|
|
322
|
+
* missing/invalid provider API keys).
|
|
322
323
|
* Returns true if token is accepted, false if 401.
|
|
323
324
|
*/
|
|
324
325
|
async function verifyGatewayToken(port, token) {
|
|
325
326
|
try {
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
const res = await fetch(`http://127.0.0.1:${port}/v1/chat/completions`, {
|
|
330
|
-
method: 'POST',
|
|
331
|
-
headers: {
|
|
332
|
-
'Content-Type': 'application/json',
|
|
333
|
-
'Authorization': `Bearer ${token}`,
|
|
334
|
-
'x-openclaw-agent-id': 'main',
|
|
335
|
-
},
|
|
336
|
-
body: JSON.stringify({
|
|
337
|
-
messages: [{ role: 'user', content: 'ping' }],
|
|
338
|
-
max_tokens: 1,
|
|
339
|
-
}),
|
|
340
|
-
signal: controller.signal,
|
|
327
|
+
const res = await fetch(`http://127.0.0.1:${port}/v1/models`, {
|
|
328
|
+
headers: { 'Authorization': `Bearer ${token}` },
|
|
329
|
+
signal: AbortSignal.timeout(3000),
|
|
341
330
|
});
|
|
342
|
-
|
|
343
|
-
// 401 = token rejected. Anything else (200, 400, 500, etc.) = token accepted.
|
|
331
|
+
// 401 = gateway rejected our token. Anything else = token accepted.
|
|
344
332
|
const accepted = res.status !== 401;
|
|
345
333
|
if (!accepted) {
|
|
346
334
|
const body = await res.text().catch(() => '');
|