// ==UserScript==
// @name Claude Code 安装助手
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 一键安装和配置 Claude Code
// @author 北枫枫
// @match *://*/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// 创建安装按钮容器
function createInstallButton() {
// 检查域名是否匹配 - 只允许 api.ikuncode.cc 和 h.ikuncode.cc
const hostname = window.location.hostname;
const allowedHosts = ['api.ikuncode.cc', 'hk.ikuncode.cc'];
if (!allowedHosts.includes(hostname)) {
console.log('不在允许的域名下,跳过按钮创建');
return;
}
// 创建容器
const container = document.createElement('div');
container.id = 'claude-installer-container';
container.style.cssText = `
position: fixed;
top: 13%;
right: 20px;
transform: translateY(-50%);
z-index: 10000;
display: flex;
align-items: stretch;
background: #007bff;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,123,255,0.3);
overflow: hidden;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
`;
// 创建主按钮
const button = document.createElement('button');
button.id = 'claude-installer-btn';
button.textContent = '安装 Claude Code';
button.style.cssText = `
padding: 12px 20px;
background: transparent;
color: white;
border: none;
cursor: pointer;
font-size: 15px;
font-weight: 500;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
white-space: nowrap;
overflow: hidden;
max-width: 200px;
opacity: 1;
`;
// 创建分隔线
const divider = document.createElement('div');
divider.id = 'claude-divider';
divider.style.cssText = `
width: 1px;
background: rgba(255, 255, 255, 0.3);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
`;
// 创建折叠按钮
const collapseBtn = document.createElement('button');
collapseBtn.id = 'claude-collapse-btn';
collapseBtn.innerHTML = '◄';
collapseBtn.style.cssText = `
width: 45px;
padding: 0;
background: transparent;
color: white;
border: none;
cursor: pointer;
font-size: 16px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
align-items: center;
justify-content: center;
`;
// 悬停效果
button.addEventListener('mouseenter', () => {
button.style.background = 'rgba(255, 255, 255, 0.1)';
});
button.addEventListener('mouseleave', () => {
button.style.background = 'transparent';
});
collapseBtn.addEventListener('mouseenter', () => {
collapseBtn.style.background = 'rgba(255, 255, 255, 0.1)';
});
collapseBtn.addEventListener('mouseleave', () => {
collapseBtn.style.background = 'transparent';
});
// 添加点击事件
button.addEventListener('click', showInstallDialog);
collapseBtn.addEventListener('click', toggleCollapse);
// 组装容器
container.appendChild(button);
container.appendChild(divider);
container.appendChild(collapseBtn);
document.body.appendChild(container);
}
// 折叠/展开功能
function toggleCollapse() {
const button = document.getElementById('claude-installer-btn');
const collapseBtn = document.getElementById('claude-collapse-btn');
const divider = document.getElementById('claude-divider');
if (button.style.maxWidth === '0px') {
// 展开
button.style.maxWidth = '200px';
button.style.padding = '12px 20px';
button.style.opacity = '1';
divider.style.width = '1px';
divider.style.opacity = '1';
collapseBtn.innerHTML = '◄';
collapseBtn.title = '折叠';
} else {
// 折叠
button.style.maxWidth = '0px';
button.style.padding = '12px 0';
button.style.opacity = '0';
divider.style.width = '0px';
divider.style.opacity = '0';
collapseBtn.innerHTML = '►';
collapseBtn.title = '展开 Claude Code 安装助手';
}
}
// 获取用户ID的辅助函数
function getUserIdFromPage() {
let userId = null;
// 直接从localStorage中的user对象获取ID
try {
const userStr = localStorage.getItem('user');
if (userStr) {
const user = JSON.parse(userStr);
if (user && user.id) {
userId = user.id.toString();
console.log('从localStorage[user]获取到用户ID:', userId);
}
}
} catch (e) {
console.log('解析localStorage[user]失败:', e);
}
return userId;
}
// 获取用户的API密钥
async function fetchUserApiKeys() {
try {
// 检查是否在 ikuncode.cc 域名下
if (!window.location.hostname.includes('ikuncode.cc')) {
console.log('不在 ikuncode.cc 域名下,跳过自动获取');
return null;
}
console.log('正在获取API密钥...');
// 获取用户ID
let userId = getUserIdFromPage();
if (!userId) {
console.log('未找到用户ID,无法获取API密钥');
return null;
}
const headers = {
'accept': 'application/json, text/plain, */*',
'cache-control': 'no-store',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'veloera-user': userId
};
console.log('使用用户ID:', userId);
const response = await fetch('/api/token/?p=0&size=10', {
method: 'GET',
headers: headers,
credentials: 'include'
});
console.log('API响应状态:', response.status);
if (response.ok) {
const data = await response.json();
console.log('获取到的API数据:', data);
return data;
} else {
const errorText = await response.text();
console.log('API请求失败:', response.status, response.statusText, errorText);
return null;
}
} catch (error) {
console.log('获取API密钥失败:', error);
return null;
}
}
// 显示API密钥选择器
function showApiKeySelector(apiKeys) {
if (!apiKeys || !apiKeys.data || apiKeys.data.length === 0) {
return false;
}
const selector = document.createElement('select');
selector.id = 'apiKeySelect';
selector.style.cssText = `
width: 100%;
padding: 8px;
margin: 10px 0;
border: 1px solid #ddd;
border-radius: 4px;
`;
// 添加默认选项
const defaultOption = document.createElement('option');
defaultOption.value = '';
defaultOption.textContent = '选择API密钥';
selector.appendChild(defaultOption);
// 添加API密钥选项
apiKeys.data.forEach(key => {
const option = document.createElement('option');
// 确保获取完整的token值
const tokenValue = key.token || key.key || key.name || key.id;
option.value = tokenValue;
// 显示友好的名称和部分token
const displayName = key.name || key.title || '未命名密钥';
const displayToken = key.token ? key.token.substring(0, 20) + '...' : `ID: ${key.id}`;
option.textContent = `${displayName} (${displayToken})`;
// 添加调试信息
console.log('添加API密钥选项:', {
name: displayName,
value: tokenValue,
fullKey: key
});
selector.appendChild(option);
});
return selector;
}
// 显示安装对话框
async function showInstallDialog() {
// 先显示加载中的对话框
const dialog = document.createElement('div');
dialog.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10001;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
max-width: 500px;
width: 90%;
`;
// 添加背景遮罩
const overlay = document.createElement('div');
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 10000;
`;
document.body.appendChild(overlay);
document.body.appendChild(dialog);
// 显示加载状态
dialog.innerHTML = `
<h3>Claude Code 安装助手</h3>
<p>正在获取API密钥...</p>
`;
// 尝试获取用户的API密钥
const apiKeys = await fetchUserApiKeys();
const hasApiKeys = apiKeys && apiKeys.data && apiKeys.data.length > 0;
console.log('hasApiKeys:', hasApiKeys);
if (hasApiKeys) {
console.log('API密钥数量:', apiKeys.data.length);
}
// 更新对话框内容
dialog.innerHTML = `
<h3>Claude Code 安装助手</h3>
<p>此脚本将生成安装命令和配置文件</p>
${hasApiKeys ?
'<label for="apiKeySelect">选择API密钥:</label>' +
'<div id="apiKeySelectContainer"></div>' +
'<p style="font-size: 12px; color: #666; margin: 5px 0;">或者手动输入:</p>'
: ''
}
<label for="apiKey">API 令牌:</label>
<input type="text" id="apiKey" placeholder="输入您的 ANTHROPIC_API_KEY" style="width: 100%; padding: 8px; margin: 10px 0; border: 1px solid #ddd; border-radius: 4px;">
<label for="baseUrl">API 渠道:</label>
<select id="baseUrl" style="width: 100%; padding: 8px; margin: 10px 0; border: 1px solid #ddd; border-radius: 4px;">
<option value="https://api.ikuncode.cc">https://api.ikuncode.cc</option>
<option value="https://hk.ikuncode.cc">https://hk.ikuncode.cc</option>
</select>
<div style="margin: 15px 0;">
<button id="generateScript" style="padding: 10px 15px; background: #28a745; color: white; border: none; border-radius: 4px; margin-right: 10px;">生成脚本</button>
<button id="downloadConfig" style="padding: 10px 15px; background: #17a2b8; color: white; border: none; border-radius: 4px; margin-right: 10px;">下载配置</button>
<button id="closeDialog" style="padding: 10px 15px; background: #6c757d; color: white; border: none; border-radius: 4px;">关闭</button>
</div>
<div id="output" style="margin-top: 15px; padding: 10px; background: #f8f9fa; border-radius: 4px; display: none;">
<h4>安装步骤:</h4>
<ol id="stepList"></ol>
</div>
`;
// 如果有API密钥,添加选择器
if (hasApiKeys) {
const selector = showApiKeySelector(apiKeys);
if (selector) {
const container = dialog.querySelector('#apiKeySelectContainer');
container.appendChild(selector);
// 监听选择器变化
selector.addEventListener('change', function() {
const apiKeyInput = document.getElementById('apiKey');
if (this.value) {
console.log('选择的API密钥值:', this.value);
apiKeyInput.value = this.value;
console.log('已填入输入框:', apiKeyInput.value);
// 视觉提示用户已自动填入
apiKeyInput.style.backgroundColor = '#e8f5e8';
setTimeout(() => {
apiKeyInput.style.backgroundColor = '';
}, 1000);
} else {
// 清空输入框
apiKeyInput.value = '';
console.log('已清空输入框');
}
});
}
}
// 事件监听
dialog.querySelector('#generateScript').addEventListener('click', generateInstallScript);
dialog.querySelector('#downloadConfig').addEventListener('click', downloadConfigFiles);
dialog.querySelector('#closeDialog').addEventListener('click', () => {
document.body.removeChild(overlay);
document.body.removeChild(dialog);
});
}
// 生成安装脚本
function generateInstallScript() {
const apiKey = document.getElementById('apiKey').value;
const baseUrl = document.getElementById('baseUrl').value;
console.log('生成脚本时使用的API密钥:', apiKey);
console.log('生成脚本时使用的Base URL:', baseUrl);
if (!apiKey) {
alert('请输入 API 令牌');
return;
}
const output = document.getElementById('output');
const stepList = document.getElementById('stepList');
stepList.innerHTML = `
<li>运行: <code>npm install -g @anthropic-ai/claude-code</code></li>
<li>创建目录: <code>mkdir -p ~/.claude</code></li>
<li>脚本自动创建配置文件并写入API密钥</li>
<li>运行: <code>claude</code></li>
`;
output.style.display = 'block';
// 生成完整的安装脚本并下载
const scriptContent = generateBashScript(apiKey, baseUrl);
console.log('生成的脚本内容包含API密钥:', scriptContent.includes(apiKey));
downloadFile('setup-claude.sh', scriptContent, 'text/plain');
// 提示用户
setTimeout(() => {
alert(`脚本已生成并下载!
使用的API密钥: ${apiKey.substring(0, 20)}...
使用的API渠道: ${baseUrl}
运行步骤:
1. chmod +x setup-claude.sh
2. ./setup-claude.sh`);
}, 500);
}
// 生成 Bash 脚本内容
function generateBashScript(apiKey, baseUrl) {
return `#!/bin/bash
# IKunCode Claude Code 安装脚本
set -e
echo "======================================"
echo " IKunCode Claude Code Setup"
echo "======================================"
echo
# 使用预设的 API 令牌
ANTHROPIC_API_KEY="${apiKey}"
ANTHROPIC_BASE_URL="${baseUrl}"
if [ -z "$ANTHROPIC_API_KEY" ]; then
echo "错误:API 令牌不能为空"
exit 1
fi
# 首先检查 claude 命令是否存在
if ! command -v claude &> /dev/null; then
echo "Claude Code 未安装。"
# 检查 npm 是否存在
if ! command -v npm &> /dev/null; then
echo "错误:npm 未安装。"
echo "请先安装 Node.js。"
exit 1
fi
echo "发现 npm: $(npm -v)"
echo "正在安装 Claude Code..."
npm install -g @anthropic-ai/claude-code
if ! command -v claude &> /dev/null; then
echo "Claude Code 安装失败"
exit 1
fi
echo "✓ Claude Code 安装成功"
else
echo "✓ Claude Code 已安装"
fi
# 创建 .claude 目录
CLAUDE_DIR="$HOME/.claude"
if [ ! -d "$CLAUDE_DIR" ]; then
mkdir -p "$CLAUDE_DIR"
echo "创建目录: ~/.claude"
fi
# 创建或更新 settings.json
SETTINGS_FILE="$CLAUDE_DIR/settings.json"
if [ -f "$SETTINGS_FILE" ]; then
echo "正在更新 settings.json..."
# 备份现有文件
cp "$SETTINGS_FILE" "$SETTINGS_FILE.bak"
fi
cat > "$SETTINGS_FILE" << EOF
{
"env": {
"DISABLE_TELEMETRY": "1",
"ANTHROPIC_AUTH_TOKEN": "$ANTHROPIC_API_KEY",
"ANTHROPIC_BASE_URL": "$ANTHROPIC_BASE_URL"
}
}
EOF
echo "✓ settings.json 配置完成"
# 创建 .claude.json
CLAUDE_JSON_FILE="$HOME/.claude.json"
if [ -f "$CLAUDE_JSON_FILE" ]; then
echo "正在更新 .claude.json..."
# 备份现有文件
cp "$CLAUDE_JSON_FILE" "$CLAUDE_JSON_FILE.bak"
fi
echo "✓ .claude.json 创建完成"
# 验证配置
if [ -f "$SETTINGS_FILE" ] && [ -f "$CLAUDE_JSON_FILE" ]; then
echo
echo "======================================"
echo " 安装完成!"
echo "======================================"
echo
echo "使用的API渠道: $ANTHROPIC_BASE_URL"
echo
echo "现在可以使用以下命令启动 Claude Code:"
echo " claude"
echo
else
echo "配置不完整"
[ ! -f "$SETTINGS_FILE" ] && echo " 缺失文件: ~/.claude/settings.json"
[ ! -f "$CLAUDE_JSON_FILE" ] && echo " 缺失文件: ~/.claude.json"
exit 1
fi
`;
}
// 下载配置文件
function downloadConfigFiles() {
const apiKey = document.getElementById('apiKey').value;
const baseUrl = document.getElementById('baseUrl').value;
if (!apiKey) {
alert('请输入 API 令牌');
return;
}
// settings.json
const settingsConfig = {
"env": {
"DISABLE_TELEMETRY": "1",
"ANTHROPIC_AUTH_TOKEN": apiKey,
"ANTHROPIC_BASE_URL": baseUrl
}
};
downloadFile('settings.json', JSON.stringify(settingsConfig, null, 2), 'application/json');
// 显示移动指令
setTimeout(() => {
alert(`配置文件已下载!
使用的API渠道: ${baseUrl}
请按以下步骤操作:
1. 创建 Claude 配置目录:
mkdir -p ~/.claude
2. 将下载的 settings.json 文件移动到配置目录:
mv ~/Downloads/settings.json ~/.claude/
3. 验证配置:
ls -la ~/.claude/
4. 运行 Claude Code:
claude`);
}, 100);
}
// 下载文件函数
function downloadFile(filename, content, mimeType) {
const blob = new Blob([content], { type: mimeType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// 页面加载完成后创建按钮
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', createInstallButton);
} else {
createInstallButton();
}
})();