- Setup wizard with auto-detection of OpenClaw paths and Claude CLI - Token sync watcher (inotifywait) for real-time credential updates - Auto-refresh trigger timer that runs Claude CLI every 30 min - Supports Claude CLI in Docker container or on host - Temporary ANTHROPIC_BASE_URL override for container environments - Anthropic model configuration for OpenClaw - Auth profile management (fixes key vs access field) - Systemd services and timers for both sync and trigger - Comprehensive documentation and troubleshooting guides - Re-authentication notification system Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
110 lines
2.9 KiB
JavaScript
110 lines
2.9 KiB
JavaScript
#!/usr/bin/env node
|
|
// test-anthropic-connection.mjs — Test Anthropic API connectivity with OAuth token
|
|
// Usage: node test-anthropic-connection.mjs [token]
|
|
// If no token provided, reads from oauth.json or .env
|
|
|
|
import https from 'node:https';
|
|
import fs from 'node:fs';
|
|
|
|
// Find token
|
|
let token = process.argv[2];
|
|
|
|
if (!token) {
|
|
// Try oauth.json
|
|
for (const path of [
|
|
'/root/.openclaw/credentials/oauth.json',
|
|
'/home/node/.openclaw/credentials/oauth.json',
|
|
]) {
|
|
try {
|
|
const data = JSON.parse(fs.readFileSync(path, 'utf8'));
|
|
token = data.anthropic?.access;
|
|
if (token) { console.log(`Token from: ${path}`); break; }
|
|
} catch {}
|
|
}
|
|
}
|
|
|
|
if (!token) {
|
|
// Try .env
|
|
try {
|
|
const env = fs.readFileSync('/root/openclaw/.env', 'utf8');
|
|
const match = env.match(/ANTHROPIC_OAUTH_TOKEN="?([^"\n]+)/);
|
|
if (match) { token = match[1]; console.log('Token from: .env'); }
|
|
} catch {}
|
|
}
|
|
|
|
if (!token) {
|
|
console.error('ERROR: No token found. Provide as argument or ensure oauth.json/.env exists.');
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(`Token: ${token.substring(0, 20)}...`);
|
|
console.log('');
|
|
|
|
// Test API
|
|
const body = JSON.stringify({
|
|
model: 'claude-sonnet-4-20250514',
|
|
max_tokens: 20,
|
|
messages: [{ role: 'user', content: 'Say "hello" and nothing else.' }],
|
|
});
|
|
|
|
const isOAuth = token.startsWith('sk-ant-oat');
|
|
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
'anthropic-version': '2023-06-01',
|
|
'Content-Length': Buffer.byteLength(body),
|
|
};
|
|
|
|
if (isOAuth) {
|
|
headers['Authorization'] = `Bearer ${token}`;
|
|
// Claude Code identity headers required for OAuth
|
|
headers['anthropic-beta'] = 'claude-code-20250219,oauth-2025-04-20';
|
|
headers['user-agent'] = 'claude-cli/2.1.0';
|
|
headers['x-app'] = 'cli';
|
|
console.log('Auth: Bearer (OAuth token)');
|
|
} else {
|
|
headers['x-api-key'] = token;
|
|
console.log('Auth: x-api-key');
|
|
}
|
|
|
|
console.log('Sending test request to api.anthropic.com...');
|
|
console.log('');
|
|
|
|
const req = https.request({
|
|
hostname: 'api.anthropic.com',
|
|
path: '/v1/messages',
|
|
method: 'POST',
|
|
headers,
|
|
}, (res) => {
|
|
let data = '';
|
|
res.on('data', (chunk) => data += chunk);
|
|
res.on('end', () => {
|
|
console.log(`Status: ${res.statusCode}`);
|
|
if (res.statusCode === 200) {
|
|
try {
|
|
const parsed = JSON.parse(data);
|
|
const text = parsed.content?.[0]?.text || '';
|
|
console.log(`Response: "${text}"`);
|
|
console.log(`Model: ${parsed.model}`);
|
|
console.log('');
|
|
console.log('SUCCESS: Anthropic API connection working');
|
|
} catch {
|
|
console.log('Response:', data.substring(0, 200));
|
|
}
|
|
} else {
|
|
console.log('Response:', data.substring(0, 500));
|
|
console.log('');
|
|
console.log('FAILED: API returned non-200 status');
|
|
process.exit(1);
|
|
}
|
|
});
|
|
});
|
|
|
|
req.on('error', (err) => {
|
|
console.error('Connection error:', err.message);
|
|
process.exit(1);
|
|
});
|
|
|
|
req.write(body);
|
|
req.end();
|