claude-code-remote-remake/taskping.js

931 lines
35 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
/**
* TaskPing - Claude Code Smart Notification System
* Main entry point for the CLI tool
*/
const Logger = require('./src/core/logger');
const Notifier = require('./src/core/notifier');
const ConfigManager = require('./src/core/config');
class TaskPingCLI {
constructor() {
this.logger = new Logger('CLI');
this.config = new ConfigManager();
this.notifier = new Notifier(this.config);
}
async init() {
// Load configuration
this.config.load();
// Initialize channels
await this.notifier.initializeChannels();
}
async run() {
const args = process.argv.slice(2);
const command = args[0];
try {
await this.init();
switch (command) {
case 'notify':
await this.handleNotify(args.slice(1));
break;
case 'test':
await this.handleTest(args.slice(1));
break;
case 'status':
await this.handleStatus(args.slice(1));
break;
case 'config':
await this.handleConfig(args.slice(1));
break;
case 'install':
await this.handleInstall(args.slice(1));
break;
case 'relay':
await this.handleRelay(args.slice(1));
break;
case 'edit-config':
await this.handleEditConfig(args.slice(1));
break;
case 'setup-email':
await this.handleSetupEmail(args.slice(1));
break;
case 'daemon':
await this.handleDaemon(args.slice(1));
break;
case 'commands':
await this.handleCommands(args.slice(1));
break;
case 'test-paste':
await this.handleTestPaste(args.slice(1));
break;
case 'test-simple':
await this.handleTestSimple(args.slice(1));
break;
case 'test-claude':
await this.handleTestClaude(args.slice(1));
break;
case 'setup-permissions':
await this.handleSetupPermissions(args.slice(1));
break;
case 'diagnose':
await this.handleDiagnose(args.slice(1));
break;
case '--help':
case '-h':
case undefined:
this.showHelp();
break;
default:
console.error(`Unknown command: ${command}`);
this.showHelp();
process.exit(1);
}
} catch (error) {
this.logger.error('CLI error:', error.message);
process.exit(1);
}
}
async handleNotify(args) {
const typeIndex = args.findIndex(arg => arg === '--type');
if (typeIndex === -1 || typeIndex + 1 >= args.length) {
console.error('Usage: taskping notify --type <completed|waiting>');
process.exit(1);
}
const type = args[typeIndex + 1];
if (!['completed', 'waiting'].includes(type)) {
console.error('Invalid type. Use: completed or waiting');
process.exit(1);
}
const result = await this.notifier.notify(type);
if (result.success) {
this.logger.info(`${type} notification sent successfully`);
process.exit(0);
} else {
this.logger.error(`Failed to send ${type} notification`);
process.exit(1);
}
}
async handleTest(args) {
console.log('Testing notification channels...\n');
const results = await this.notifier.test();
for (const [channel, result] of Object.entries(results)) {
const status = result.success ? '✅ PASS' : '❌ FAIL';
console.log(`${channel}: ${status}`);
if (result.error) {
console.log(` Error: ${result.error}`);
}
}
const passCount = Object.values(results).filter(r => r.success).length;
const totalCount = Object.keys(results).length;
console.log(`\nTest completed: ${passCount}/${totalCount} channels passed`);
if (passCount === 0) {
process.exit(1);
}
}
async handleStatus(args) {
const status = this.notifier.getStatus();
console.log('TaskPing Status\n');
console.log('Configuration:');
console.log(` Enabled: ${status.enabled ? 'Yes' : 'No'}`);
console.log(` Language: ${status.config.language}`);
console.log(` Sounds: ${status.config.sound.completed} / ${status.config.sound.waiting}`);
console.log('\nChannels:');
// 显示所有可用的渠道,包括未启用的
const allChannels = this.config._channels || {};
const activeChannels = status.channels || {};
// 合并所有渠道信息
const channelNames = new Set([
...Object.keys(allChannels),
...Object.keys(activeChannels)
]);
for (const name of channelNames) {
const channelConfig = allChannels[name] || {};
const channelStatus = activeChannels[name];
let enabled, configured, relay;
if (channelStatus) {
// 活跃渠道,使用实际状态
enabled = channelStatus.enabled ? '✅' : '❌';
configured = channelStatus.configured ? '✅' : '❌';
relay = channelStatus.supportsRelay ? '✅' : '❌';
} else {
// 非活跃渠道,使用配置状态
enabled = channelConfig.enabled ? '✅' : '❌';
configured = this._isChannelConfigured(name, channelConfig) ? '✅' : '❌';
relay = this._supportsRelay(name) ? '✅' : '❌';
}
console.log(` ${name}:`);
console.log(` Enabled: ${enabled}`);
console.log(` Configured: ${configured}`);
console.log(` Supports Relay: ${relay}`);
}
}
_isChannelConfigured(name, config) {
switch (name) {
case 'desktop':
return true; // 桌面通知不需要特殊配置
case 'email':
return config.config &&
config.config.smtp &&
config.config.smtp.host &&
config.config.smtp.auth &&
config.config.smtp.auth.user &&
config.config.to;
default:
return false;
}
}
_supportsRelay(name) {
switch (name) {
case 'email':
return true;
case 'desktop':
default:
return false;
}
}
async handleConfig(args) {
// Launch the configuration tool
const ConfigTool = require('./src/tools/config-manager');
const configTool = new ConfigTool(this.config);
await configTool.run(args);
}
async handleInstall(args) {
// Launch the installer
const Installer = require('./src/tools/installer');
const installer = new Installer(this.config);
await installer.run(args);
}
async handleRelay(args) {
const subcommand = args[0];
switch (subcommand) {
case 'start':
await this.startRelay(args.slice(1));
break;
case 'stop':
await this.stopRelay(args.slice(1));
break;
case 'status':
await this.relayStatus(args.slice(1));
break;
case 'cleanup':
await this.cleanupRelay(args.slice(1));
break;
default:
console.error('Usage: taskping relay <start|stop|status|cleanup>');
console.log('');
console.log('Commands:');
console.log(' start 启动邮件命令中继服务');
console.log(' stop 停止邮件命令中继服务');
console.log(' status 查看中继服务状态');
console.log(' cleanup 清理已完成的命令历史');
process.exit(1);
}
}
async startRelay(args) {
try {
const CommandRelayService = require('./src/relay/command-relay');
const emailConfig = this.config.getChannel('email');
if (!emailConfig || !emailConfig.enabled) {
console.error('❌ 邮件渠道未配置或未启用');
console.log('请先运行: taskping config');
process.exit(1);
}
console.log('🚀 启动邮件命令中继服务...');
const relayService = new CommandRelayService(emailConfig.config);
// 监听事件
relayService.on('started', () => {
console.log('✅ 命令中继服务已启动');
console.log('📧 正在监听邮件回复...');
console.log('💡 现在您可以通过回复邮件来远程执行Claude Code命令');
console.log('');
console.log('按 Ctrl+C 停止服务');
});
relayService.on('commandQueued', (command) => {
console.log(`📨 收到新命令: ${command.command.substring(0, 50)}...`);
});
relayService.on('commandExecuted', (command) => {
console.log(`✅ 命令执行成功: ${command.id}`);
});
relayService.on('commandFailed', (command, error) => {
console.log(`❌ 命令执行失败: ${command.id} - ${error.message}`);
});
// 处理优雅关闭
process.on('SIGINT', async () => {
console.log('\n🛑 正在停止命令中继服务...');
await relayService.stop();
console.log('✅ 服务已停止');
process.exit(0);
});
// 启动服务
await relayService.start();
// 保持进程运行
process.stdin.resume();
} catch (error) {
console.error('❌ 启动中继服务失败:', error.message);
process.exit(1);
}
}
async stopRelay(args) {
console.log('💡 命令中继服务通常通过 Ctrl+C 停止');
console.log('如果服务仍在运行,请找到对应的进程并手动终止');
}
async relayStatus(args) {
try {
const fs = require('fs');
const path = require('path');
const stateFile = path.join(__dirname, 'src/data/relay-state.json');
console.log('📊 命令中继服务状态\n');
// 检查邮件配置
const emailConfig = this.config.getChannel('email');
if (!emailConfig || !emailConfig.enabled) {
console.log('❌ 邮件渠道未配置');
return;
}
console.log('✅ 邮件配置已启用');
console.log(`📧 SMTP: ${emailConfig.config.smtp.host}:${emailConfig.config.smtp.port}`);
console.log(`📥 IMAP: ${emailConfig.config.imap.host}:${emailConfig.config.imap.port}`);
console.log(`📬 收件人: ${emailConfig.config.to}`);
// 检查中继状态
if (fs.existsSync(stateFile)) {
const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
console.log(`\n📋 命令队列: ${state.commandQueue?.length || 0} 个命令`);
if (state.commandQueue && state.commandQueue.length > 0) {
console.log('\n最近的命令:');
state.commandQueue.slice(-5).forEach(cmd => {
const status = cmd.status === 'completed' ? '✅' :
cmd.status === 'failed' ? '❌' :
cmd.status === 'executing' ? '⏳' : '⏸️';
console.log(` ${status} ${cmd.id}: ${cmd.command.substring(0, 50)}...`);
});
}
} else {
console.log('\n📋 无命令历史记录');
}
} catch (error) {
console.error('❌ 获取状态失败:', error.message);
}
}
async cleanupRelay(args) {
try {
const fs = require('fs');
const path = require('path');
const stateFile = path.join(__dirname, 'src/data/relay-state.json');
if (!fs.existsSync(stateFile)) {
console.log('📋 无需清理,没有找到命令历史');
return;
}
const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
const beforeCount = state.commandQueue?.length || 0;
// 清理已完成的命令 (保留24小时内的)
const cutoff = new Date(Date.now() - 24 * 60 * 60 * 1000);
state.commandQueue = (state.commandQueue || []).filter(cmd =>
cmd.status !== 'completed' ||
new Date(cmd.completedAt || cmd.queuedAt) > cutoff
);
const afterCount = state.commandQueue.length;
const removedCount = beforeCount - afterCount;
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
console.log(`🧹 清理完成: 移除了 ${removedCount} 个已完成的命令`);
console.log(`📋 剩余 ${afterCount} 个命令在队列中`);
} catch (error) {
console.error('❌ 清理失败:', error.message);
}
}
async handleEditConfig(args) {
const { spawn } = require('child_process');
const path = require('path');
const configType = args[0];
if (!configType) {
console.log('可用的配置文件:');
console.log(' user - 用户个人配置 (config/user.json)');
console.log(' channels - 通知渠道配置 (config/channels.json)');
console.log(' default - 默认配置模板 (config/default.json)');
console.log('');
console.log('使用方法: taskping edit-config <配置类型>');
console.log('例如: taskping edit-config channels');
return;
}
const configFiles = {
'user': path.join(__dirname, 'config/user.json'),
'channels': path.join(__dirname, 'config/channels.json'),
'default': path.join(__dirname, 'config/default.json')
};
const configFile = configFiles[configType];
if (!configFile) {
console.error('❌ 无效的配置类型:', configType);
console.log('可用类型: user, channels, default');
return;
}
// 检查文件是否存在
const fs = require('fs');
if (!fs.existsSync(configFile)) {
console.error('❌ 配置文件不存在:', configFile);
return;
}
console.log(`📝 正在打开配置文件: ${configFile}`);
console.log('💡 编辑完成后保存并关闭编辑器即可生效');
console.log('');
// 确定使用的编辑器
const editor = process.env.EDITOR || process.env.VISUAL || this._getDefaultEditor();
try {
const editorProcess = spawn(editor, [configFile], {
stdio: 'inherit'
});
editorProcess.on('close', (code) => {
if (code === 0) {
console.log('✅ 配置文件已保存');
console.log('💡 运行 "taskping status" 查看更新后的配置');
} else {
console.log('❌ 编辑器异常退出');
}
});
editorProcess.on('error', (error) => {
console.error('❌ 无法启动编辑器:', error.message);
console.log('');
console.log('💡 你可以手动编辑配置文件:');
console.log(` ${configFile}`);
});
} catch (error) {
console.error('❌ 启动编辑器失败:', error.message);
console.log('');
console.log('💡 你可以手动编辑配置文件:');
console.log(` ${configFile}`);
}
}
_getDefaultEditor() {
// 根据平台确定默认编辑器
if (process.platform === 'win32') {
return 'notepad';
} else if (process.platform === 'darwin') {
return 'nano'; // 在macOS上使用nano因为大多数用户都有
} else {
return 'nano'; // Linux默认使用nano
}
}
async handleSetupEmail(args) {
const readline = require('readline');
const fs = require('fs');
const path = require('path');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const question = (prompt) => {
return new Promise((resolve) => {
rl.question(prompt, resolve);
});
};
try {
console.log('🚀 TaskPing 邮件快速配置向导\n');
// 选择邮箱提供商
console.log('请选择您的邮箱提供商:');
console.log('1. Gmail');
console.log('2. QQ邮箱');
console.log('3. 163邮箱');
console.log('4. Outlook/Hotmail');
console.log('5. 自定义');
const providerChoice = await question('\n请选择 (1-5): ');
let smtpHost, smtpPort, imapHost, imapPort, secure;
switch (providerChoice) {
case '1':
smtpHost = 'smtp.gmail.com';
smtpPort = 587;
imapHost = 'imap.gmail.com';
imapPort = 993;
secure = false;
console.log('\n📧 Gmail 配置');
console.log('💡 需要先启用两步验证并生成应用密码');
break;
case '2':
smtpHost = 'smtp.qq.com';
smtpPort = 587;
imapHost = 'imap.qq.com';
imapPort = 993;
secure = false;
console.log('\n📧 QQ邮箱配置');
break;
case '3':
smtpHost = 'smtp.163.com';
smtpPort = 587;
imapHost = 'imap.163.com';
imapPort = 993;
secure = false;
console.log('\n📧 163邮箱配置');
break;
case '4':
smtpHost = 'smtp.live.com';
smtpPort = 587;
imapHost = 'imap-mail.outlook.com';
imapPort = 993;
secure = false;
console.log('\n📧 Outlook 配置');
break;
case '5':
console.log('\n📧 自定义配置');
smtpHost = await question('SMTP 主机: ');
smtpPort = parseInt(await question('SMTP 端口 (默认587): ') || '587');
imapHost = await question('IMAP 主机: ');
imapPort = parseInt(await question('IMAP 端口 (默认993): ') || '993');
const secureInput = await question('使用 SSL/TLS? (y/n): ');
secure = secureInput.toLowerCase() === 'y';
break;
default:
console.log('❌ 无效选择');
rl.close();
return;
}
// 获取邮箱账户信息
console.log('\n📝 请输入邮箱账户信息:');
const email = await question('邮箱地址: ');
const password = await question('密码/应用密码: ');
// 构建配置
const emailConfig = {
type: "email",
enabled: true,
config: {
smtp: {
host: smtpHost,
port: smtpPort,
secure: secure,
auth: {
user: email,
pass: password
}
},
imap: {
host: imapHost,
port: imapPort,
secure: true,
auth: {
user: email,
pass: password
}
},
from: `TaskPing <${email}>`,
to: email,
template: {
checkInterval: 30
}
}
};
// 读取现有配置
const channelsFile = path.join(__dirname, 'config/channels.json');
let channels = {};
if (fs.existsSync(channelsFile)) {
channels = JSON.parse(fs.readFileSync(channelsFile, 'utf8'));
}
// 更新邮件配置
channels.email = emailConfig;
// 保存配置
fs.writeFileSync(channelsFile, JSON.stringify(channels, null, 2));
console.log('\n✅ 邮件配置已保存!');
console.log('\n🧪 现在可以测试邮件功能:');
console.log(' taskping test');
console.log('\n🚀 启动命令中继服务:');
console.log(' taskping relay start');
// 询问是否立即测试
const testNow = await question('\n立即测试邮件发送? (y/n): ');
if (testNow.toLowerCase() === 'y') {
rl.close();
// 重新加载配置并测试
await this.init();
await this.handleTest([]);
} else {
rl.close();
}
} catch (error) {
console.error('❌ 配置失败:', error.message);
rl.close();
}
}
async handleDaemon(args) {
const TaskPingDaemon = require('./src/daemon/taskping-daemon');
const daemon = new TaskPingDaemon();
const command = args[0];
switch (command) {
case 'start':
await daemon.start();
break;
case 'stop':
await daemon.stop();
break;
case 'restart':
await daemon.restart();
break;
case 'status':
daemon.showStatus();
break;
default:
console.log('Usage: taskping daemon <start|stop|restart|status>');
console.log('');
console.log('Commands:');
console.log(' start 启动后台守护进程');
console.log(' stop 停止后台守护进程');
console.log(' restart 重启后台守护进程');
console.log(' status 查看守护进程状态');
break;
}
}
async handleCommands(args) {
const ClaudeCommandBridge = require('./src/relay/claude-command-bridge');
const bridge = new ClaudeCommandBridge();
const command = args[0];
switch (command) {
case 'list':
const pending = bridge.getPendingCommands();
console.log(`📋 待处理命令: ${pending.length}\n`);
if (pending.length > 0) {
pending.forEach((cmd, index) => {
console.log(`${index + 1}. ${cmd.id}`);
console.log(` 命令: ${cmd.command}`);
console.log(` 时间: ${cmd.timestamp}`);
console.log(` 会话: ${cmd.sessionId}`);
console.log('');
});
}
break;
case 'status':
const status = bridge.getStatus();
console.log('📊 命令桥接器状态\n');
console.log(`待处理命令: ${status.pendingCommands}`);
console.log(`已处理命令: ${status.processedCommands}`);
console.log(`命令目录: ${status.commandsDir}`);
console.log(`响应目录: ${status.responseDir}`);
if (status.recentCommands.length > 0) {
console.log('\n最近命令:');
status.recentCommands.forEach(cmd => {
console.log(`${cmd.command} (${cmd.timestamp})`);
});
}
break;
case 'cleanup':
bridge.cleanup();
console.log('🧹 已清理旧的命令文件');
break;
case 'clear':
const pending2 = bridge.getPendingCommands();
for (const cmd of pending2) {
bridge.markCommandProcessed(cmd.id, 'cancelled', 'Manually cancelled');
}
console.log(`🗑️ 已清除 ${pending2.length} 个待处理命令`);
break;
default:
console.log('Usage: taskping commands <list|status|cleanup|clear>');
console.log('');
console.log('Commands:');
console.log(' list 显示待处理的邮件命令');
console.log(' status 显示命令桥接器状态');
console.log(' cleanup 清理旧的命令文件');
console.log(' clear 清除所有待处理命令');
break;
}
}
async handleTestPaste(args) {
const ClipboardAutomation = require('./src/automation/clipboard-automation');
const automation = new ClipboardAutomation();
const testCommand = args.join(' ') || 'echo "测试邮件回复自动粘贴功能"';
console.log('🧪 测试自动粘贴功能');
console.log(`📝 测试命令: ${testCommand}`);
console.log('\n⚠ 请确保 Claude Code 或 Terminal 窗口已打开并处于活动状态');
console.log('⏳ 3 秒后自动发送命令...\n');
// 倒计时
for (let i = 3; i > 0; i--) {
process.stdout.write(`${i}... `);
await new Promise(resolve => setTimeout(resolve, 1000));
}
console.log('\n');
try {
const success = await automation.sendCommand(testCommand);
if (success) {
console.log('✅ 命令已自动粘贴!');
console.log('💡 如果没有看到效果,请检查应用权限和窗口状态');
} else {
console.log('❌ 自动粘贴失败');
console.log('💡 请确保给予自动化权限并打开目标应用');
}
} catch (error) {
console.error('❌ 测试失败:', error.message);
}
}
async handleSetupPermissions(args) {
const PermissionSetup = require('./setup-permissions');
const setup = new PermissionSetup();
await setup.checkAndSetup();
}
async handleTestSimple(args) {
const SimpleAutomation = require('./src/automation/simple-automation');
const automation = new SimpleAutomation();
const testCommand = args.join(' ') || 'echo "测试简单自动化功能"';
console.log('🧪 测试简单自动化功能');
console.log(`📝 测试命令: ${testCommand}`);
console.log('\n这个测试会');
console.log('1. 📋 将命令复制到剪贴板');
console.log('2. 📄 保存命令到文件');
console.log('3. 🔔 发送通知(包含对话框)');
console.log('4. 🤖 尝试自动粘贴(如果有权限)');
console.log('\n⏳ 开始测试...\n');
try {
const success = await automation.sendCommand(testCommand, 'test-session');
if (success) {
console.log('✅ 测试成功!');
console.log('\n📋 下一步操作:');
console.log('1. 检查是否收到了通知');
console.log('2. 检查命令是否已复制到剪贴板');
console.log('3. 如果看到对话框,可以选择打开命令文件');
console.log('4. 手动粘贴到 Claude Code 中(如果没有自动粘贴)');
const status = automation.getStatus();
console.log(`\n📄 命令文件: ${status.commandFile}`);
if (status.commandFileExists) {
console.log('💡 可以运行 "open -t ' + status.commandFile + '" 查看命令文件');
}
} else {
console.log('❌ 测试失败');
}
} catch (error) {
console.error('❌ 测试过程中发生错误:', error.message);
}
}
async handleTestClaude(args) {
const ClaudeAutomation = require('./src/automation/claude-automation');
const automation = new ClaudeAutomation();
const testCommand = args.join(' ') || 'echo "这是一个自动化测试命令,来自邮件回复"';
console.log('🤖 测试 Claude Code 专用自动化');
console.log(`📝 测试命令: ${testCommand}`);
console.log('\n⚠ 请确保:');
console.log(' 1. Claude Code 应用已打开');
console.log(' 2. 或者 Terminal/iTerm2 等终端应用已打开');
console.log(' 3. 已经给予必要的辅助功能权限');
console.log('\n⏳ 5 秒后开始完全自动化测试...\n');
// 倒计时
for (let i = 5; i > 0; i--) {
process.stdout.write(`${i}... `);
await new Promise(resolve => setTimeout(resolve, 1000));
}
console.log('\n🚀 开始自动化...\n');
try {
// 检查权限
const hasPermission = await automation.requestPermissions();
if (!hasPermission) {
console.log('⚠️ 权限检查失败,但仍会尝试执行...');
}
// 执行完全自动化
const success = await automation.sendCommand(testCommand, 'test-session');
if (success) {
console.log('✅ 完全自动化测试成功!');
console.log('💡 命令应该已经自动输入到 Claude Code 并开始执行');
console.log('🔍 请检查 Claude Code 窗口是否收到了命令');
} else {
console.log('❌ 自动化测试失败');
console.log('💡 可能的原因:');
console.log(' • 没有找到 Claude Code 或终端应用');
console.log(' • 权限不足');
console.log(' • 应用没有响应');
console.log('\n🔧 建议:');
console.log(' 1. 运行 "taskping setup-permissions" 检查权限');
console.log(' 2. 确保 Claude Code 在前台运行');
console.log(' 3. 尝试先手动在 Claude Code 中点击输入框');
}
} catch (error) {
console.error('❌ 测试过程中发生错误:', error.message);
}
}
async handleDiagnose(args) {
const AutomationDiagnostic = require('./diagnose-automation');
const diagnostic = new AutomationDiagnostic();
await diagnostic.runDiagnostic();
}
showHelp() {
console.log(`
TaskPing - Claude Code Smart Notification System
Usage: taskping <command> [options]
Commands:
notify --type <type> Send a notification (completed|waiting)
test Test all notification channels
status Show system status
config Launch configuration manager
setup-email Quick email setup wizard
edit-config <type> Edit configuration files directly
install Install and configure Claude Code hooks
relay <subcommand> Manage email command relay service
daemon <subcommand> Manage background daemon service
commands <subcommand> Manage email commands and bridge
test-paste [command] Test automatic paste functionality
test-simple [command] Test simple automation (recommended)
test-claude [command] Test Claude Code full automation
setup-permissions Setup macOS permissions for automation
diagnose Diagnose automation issues
Options:
-h, --help Show this help message
Relay Subcommands:
relay start Start email command relay service
relay stop Stop email command relay service
relay status Show relay service status
relay cleanup Clean up completed command history
Daemon Subcommands:
daemon start Start background daemon service
daemon stop Stop background daemon service
daemon restart Restart background daemon service
daemon status Show daemon service status
Commands Subcommands:
commands list Show pending email commands
commands status Show command bridge status
commands cleanup Clean up old command files
commands clear Clear all pending commands
Examples:
taskping notify --type completed
taskping test
taskping setup-email # 快速配置邮件 (推荐)
taskping edit-config channels # 直接编辑配置文件
taskping config # 交互式配置
taskping install
taskping daemon start # 启动后台服务 (推荐)
taskping daemon status # 查看服务状态
taskping test-claude # 测试完全自动化 (推荐)
taskping commands list # 查看待处理的邮件命令
taskping relay start # 前台运行 (需要保持窗口)
For more information, visit: https://github.com/TaskPing/TaskPing
`);
}
}
// Run CLI if this file is executed directly
if (require.main === module) {
const cli = new TaskPingCLI();
cli.run().catch(error => {
console.error('Fatal error:', error.message);
process.exit(1);
});
}
module.exports = TaskPingCLI;