claude-code-remote-remake/diagnose-automation.js

380 lines
14 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
/**
* 自动化功能诊断工具
* 详细检测和诊断自动粘贴功能的问题
*/
const { spawn } = require('child_process');
class AutomationDiagnostic {
constructor() {
this.tests = [];
}
async runDiagnostic() {
console.log('🔍 自动化功能诊断工具\n');
if (process.platform !== 'darwin') {
console.log('❌ 此诊断工具仅适用于 macOS');
return;
}
// 运行所有测试
await this.testBasicAppleScript();
await this.testSystemEventsAccess();
await this.testKeystrokePermission();
await this.testApplicationDetection();
await this.testClipboardAccess();
await this.testDetailedPermissions();
// 显示总结
this.showSummary();
await this.provideSolutions();
}
async testBasicAppleScript() {
console.log('1. 📋 测试基本 AppleScript 执行...');
try {
const result = await this.runAppleScript('return "AppleScript works"');
if (result === 'AppleScript works') {
console.log(' ✅ 基本 AppleScript 执行正常');
this.tests.push({ name: 'AppleScript', status: 'pass' });
} else {
console.log(' ❌ AppleScript 返回异常结果');
this.tests.push({ name: 'AppleScript', status: 'fail', error: 'Unexpected result' });
}
} catch (error) {
console.log(' ❌ AppleScript 执行失败:', error.message);
this.tests.push({ name: 'AppleScript', status: 'fail', error: error.message });
}
console.log('');
}
async testSystemEventsAccess() {
console.log('2. 🖥️ 测试 System Events 访问...');
try {
const script = `
tell application "System Events"
return name of first application process whose frontmost is true
end tell
`;
const result = await this.runAppleScript(script);
if (result && result !== 'permission_denied') {
console.log(` ✅ 可以访问 System Events当前前台应用: ${result}`);
this.tests.push({ name: 'System Events', status: 'pass', data: result });
} else {
console.log(' ❌ System Events 访问被拒绝');
this.tests.push({ name: 'System Events', status: 'fail', error: 'Access denied' });
}
} catch (error) {
console.log(' ❌ System Events 访问失败:', error.message);
this.tests.push({ name: 'System Events', status: 'fail', error: error.message });
}
console.log('');
}
async testKeystrokePermission() {
console.log('3. ⌨️ 测试按键发送权限...');
try {
const script = `
tell application "System Events"
try
keystroke "test" without key down
return "keystroke_success"
on error errorMessage
return "keystroke_failed: " & errorMessage
end try
end tell
`;
const result = await this.runAppleScript(script);
if (result === 'keystroke_success') {
console.log(' ✅ 按键发送权限正常');
this.tests.push({ name: 'Keystroke', status: 'pass' });
} else {
console.log(` ❌ 按键发送失败: ${result}`);
this.tests.push({ name: 'Keystroke', status: 'fail', error: result });
}
} catch (error) {
console.log(' ❌ 按键测试异常:', error.message);
this.tests.push({ name: 'Keystroke', status: 'fail', error: error.message });
}
console.log('');
}
async testApplicationDetection() {
console.log('4. 🔍 测试应用程序检测...');
try {
const script = `
tell application "System Events"
set appList to {"Terminal", "iTerm2", "iTerm", "Visual Studio Code", "Code", "Cursor", "Claude Code"}
set foundApps to {}
repeat with appName in appList
try
if application process appName exists then
set foundApps to foundApps & {appName}
end if
end try
end repeat
return foundApps as string
end tell
`;
const result = await this.runAppleScript(script);
if (result) {
console.log(` ✅ 检测到以下应用: ${result}`);
this.tests.push({ name: 'App Detection', status: 'pass', data: result });
} else {
console.log(' ⚠️ 未检测到目标应用程序');
this.tests.push({ name: 'App Detection', status: 'warn', error: 'No target apps found' });
}
} catch (error) {
console.log(' ❌ 应用检测失败:', error.message);
this.tests.push({ name: 'App Detection', status: 'fail', error: error.message });
}
console.log('');
}
async testClipboardAccess() {
console.log('5. 📋 测试剪贴板访问...');
try {
// 测试写入剪贴板
await this.setClipboard('test_clipboard_content');
const content = await this.getClipboard();
if (content.includes('test_clipboard_content')) {
console.log(' ✅ 剪贴板读写正常');
this.tests.push({ name: 'Clipboard', status: 'pass' });
} else {
console.log(' ❌ 剪贴板内容不匹配');
this.tests.push({ name: 'Clipboard', status: 'fail', error: 'Content mismatch' });
}
} catch (error) {
console.log(' ❌ 剪贴板访问失败:', error.message);
this.tests.push({ name: 'Clipboard', status: 'fail', error: error.message });
}
console.log('');
}
async testDetailedPermissions() {
console.log('6. 🔐 详细权限检查...');
try {
// 检查当前运行的进程
const whoami = await this.runCommand('whoami');
console.log(` 👤 当前用户: ${whoami}`);
// 检查终端应用
const terminal = process.env.TERM_PROGRAM || 'Unknown';
console.log(` 💻 终端程序: ${terminal}`);
// 检查是否在 IDE 中运行
const isInIDE = process.env.VSCODE_PID || process.env.CURSOR_SESSION_ID || process.env.JB_IDE_PID;
if (isInIDE) {
console.log(' 🔧 检测到在 IDE 中运行');
}
// 尝试获取更详细的权限信息
const script = `
tell application "System Events"
try
set frontApp to name of first application process whose frontmost is true
set allApps to name of every application process
return "Front: " & frontApp & ", All: " & (count of allApps)
on error errorMsg
return "Error: " & errorMsg
end try
end tell
`;
const permResult = await this.runAppleScript(script);
console.log(` 📊 权限测试结果: ${permResult}`);
this.tests.push({
name: 'Detailed Permissions',
status: 'info',
data: { user: whoami, terminal, result: permResult }
});
} catch (error) {
console.log(' ❌ 详细检查失败:', error.message);
this.tests.push({ name: 'Detailed Permissions', status: 'fail', error: error.message });
}
console.log('');
}
showSummary() {
console.log('📊 诊断结果总结:\n');
const passed = this.tests.filter(t => t.status === 'pass').length;
const failed = this.tests.filter(t => t.status === 'fail').length;
const warned = this.tests.filter(t => t.status === 'warn').length;
this.tests.forEach(test => {
const icon = test.status === 'pass' ? '✅' :
test.status === 'fail' ? '❌' :
test.status === 'warn' ? '⚠️' : '';
console.log(`${icon} ${test.name}`);
if (test.error) {
console.log(` 错误: ${test.error}`);
}
if (test.data) {
console.log(` 数据: ${test.data}`);
}
});
console.log(`\n📈 总计: ${passed} 通过, ${failed} 失败, ${warned} 警告\n`);
}
async provideSolutions() {
const keystrokeTest = this.tests.find(t => t.name === 'Keystroke');
const systemEventsTest = this.tests.find(t => t.name === 'System Events');
console.log('💡 解决方案建议:\n');
if (keystrokeTest && keystrokeTest.status === 'fail') {
console.log('🔧 按键发送问题解决方案:');
console.log(' 1. 打开 系统偏好设置 > 安全性与隐私 > 隐私 > 辅助功能');
console.log(' 2. 移除并重新添加你的终端应用 (Terminal/iTerm2/VS Code)');
console.log(' 3. 确保勾选框已被选中');
console.log(' 4. 重启终端应用');
console.log('');
}
if (systemEventsTest && systemEventsTest.status === 'fail') {
console.log('🔧 System Events 访问问题解决方案:');
console.log(' 1. 检查 系统偏好设置 > 安全性与隐私 > 隐私 > 自动化');
console.log(' 2. 确保你的终端应用下勾选了 "System Events"');
console.log(' 3. 如果没有看到你的应用,先运行一次自动化脚本触发权限请求');
console.log('');
}
console.log('🚀 额外建议:');
console.log(' • 尝试完全退出并重启终端应用');
console.log(' • 在 Terminal 中运行而不是在 IDE 集成终端中');
console.log(' • 检查是否有安全软件阻止自动化');
console.log(' • 尝试在不同的终端应用中运行测试');
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const answer = await this.question(rl, '\n是否尝试一个简单的修复测试(y/n): ');
if (answer.toLowerCase() === 'y') {
await this.runSimpleFixTest();
}
rl.close();
}
async runSimpleFixTest() {
console.log('\n🔨 运行简单修复测试...');
try {
// 尝试最基本的自动化
const script = `
display dialog "TaskPing 自动化测试" with title "权限测试" buttons {"确定"} default button 1 giving up after 3
`;
await this.runAppleScript(script);
console.log('✅ 基本对话框测试成功');
} catch (error) {
console.log('❌ 基本测试失败:', error.message);
}
}
async runAppleScript(script) {
return new Promise((resolve, reject) => {
const osascript = spawn('osascript', ['-e', script]);
let output = '';
let error = '';
osascript.stdout.on('data', (data) => {
output += data.toString();
});
osascript.stderr.on('data', (data) => {
error += data.toString();
});
osascript.on('close', (code) => {
if (code === 0) {
resolve(output.trim());
} else {
reject(new Error(error || `Exit code: ${code}`));
}
});
});
}
async runCommand(command) {
return new Promise((resolve, reject) => {
const proc = spawn('sh', ['-c', command]);
let output = '';
proc.stdout.on('data', (data) => {
output += data.toString();
});
proc.on('close', (code) => {
if (code === 0) {
resolve(output.trim());
} else {
reject(new Error(`Command failed with exit code ${code}`));
}
});
});
}
async setClipboard(text) {
return new Promise((resolve, reject) => {
const pbcopy = spawn('pbcopy');
pbcopy.stdin.write(text);
pbcopy.stdin.end();
pbcopy.on('close', (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error('Failed to set clipboard'));
}
});
});
}
async getClipboard() {
return new Promise((resolve, reject) => {
const pbpaste = spawn('pbpaste');
let output = '';
pbpaste.stdout.on('data', (data) => {
output += data.toString();
});
pbpaste.on('close', (code) => {
if (code === 0) {
resolve(output);
} else {
reject(new Error('Failed to get clipboard'));
}
});
});
}
question(rl, prompt) {
return new Promise(resolve => {
rl.question(prompt, resolve);
});
}
}
// 运行诊断
if (require.main === module) {
const diagnostic = new AutomationDiagnostic();
diagnostic.runDiagnostic().catch(console.error);
}
module.exports = AutomationDiagnostic;