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

380 lines
14 KiB
JavaScript
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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;