Compare commits

..

No commits in common. "e91aeabdebd9831c7237b7ca8ca2f111d7fa15d4" and "b14f95d821470c7610e0f066a0cac3b22e89b0c4" have entirely different histories.

38 changed files with 114 additions and 2083 deletions

View File

@ -1,9 +0,0 @@
{
"permissions": {
"allow": [
"WebFetch(domain:github.com)"
],
"deny": [],
"ask": []
}
}

View File

@ -69,10 +69,6 @@ TELEGRAM_BOT_TOKEN=your-telegram-bot-token
# Telegram webhook URL您的公開 HTTPS URL # Telegram webhook URL您的公開 HTTPS URL
# TELEGRAM_WEBHOOK_URL=https://your-domain.com # TELEGRAM_WEBHOOK_URL=https://your-domain.com
# 強制使用 IPv4 連接 Telegram API預設false
# 在某些網路環境下IPv6 連接可能不穩定,設置為 true 可強制使用 IPv4
# TELEGRAM_FORCE_IPV4=false
# ===== 系统配置 ===== # ===== 系统配置 =====
# 会话映射文件路径 # 会话映射文件路径
SESSION_MAP_PATH=/path/to/your/project/src/data/session-map.json SESSION_MAP_PATH=/path/to/your/project/src/data/session-map.json

View File

@ -1,167 +0,0 @@
First of all, many thanks to everyone who wants to contribute to Claude-Code-Remote!
# Contributing to Claude Code Remote
## 🚀 Quick Start
```bash
# Fork, clone, and setup
git clone https://github.com/YOUR_USERNAME/Claude-Code-Remote.git
cd Claude-Code-Remote
npm install
cp .env.example .env
# Create feature branch
git checkout -b feature/your-feature
# Test your changes
npm run webhooks
```
## 📝 Coding Standards (Automated Checks)
### 🚫 Strictly Forbidden (CI will auto-reject)
```javascript
// ❌ Hardcoded secrets/tokens
const TELEGRAM_BOT_TOKEN = "123456789:ABC...";
const LINE_CHANNEL_ACCESS_TOKEN = "abc123";
const SMTP_PASS = "mypassword";
// ❌ Hardcoded API URLs
const API_URL = "https://api.telegram.org/bot123456789";
fetch("https://hooks.slack.com/abc123");
// ❌ console.log in production code
console.log("Debug info");
console.error("Error occurred");
// ❌ Async operations without error handling
async function sendMessage() {
await fetch(url); // No try-catch
}
// ❌ String concatenation with user input
const query = "SELECT * FROM users WHERE id=" + userId;
```
### ✅ Required Standards (CI checks pass)
```javascript
// ✅ Use environment variables
const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
const LINE_TOKEN = process.env.LINE_CHANNEL_ACCESS_TOKEN;
const SMTP_PASS = process.env.SMTP_PASS;
// ✅ Use configuration files
const config = require('./config.json');
const API_URL = `${config.telegram.baseUrl}/bot${process.env.TELEGRAM_BOT_TOKEN}`;
// ✅ Use proper logging
const logger = require('./src/core/logger');
logger.info('Message sent successfully');
logger.error('Failed to send message:', error);
// ✅ Proper error handling
async function sendMessage(message) {
try {
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify({ text: message })
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
} catch (error) {
logger.error('Send message failed:', error);
throw error; // Re-throw for caller to handle
}
}
// ✅ Input validation and parameterized queries
function validateUserId(userId) {
if (!userId || typeof userId !== 'string') {
throw new Error('Invalid user ID');
}
return userId.replace(/[^a-zA-Z0-9-]/g, '');
}
```
### 🔧 Enforcement Rules
1. **Automated CI checks**: Every PR automatically checked for code quality
2. **Hardcode detection**: Auto-scan all `.js` files for sensitive data
3. **Log checking**: Prohibit `console.log` in production code
4. **Error handling**: Check async functions for proper error handling
## 📛 Naming Conventions
### Issue Title Format
```bash
[BUG] Short clear description
[FEATURE] Short clear description
Examples:
[BUG] Telegram bot not responding to commands
[FEATURE] Add Discord platform integration
```
### PR Title Format
```bash
type(scope): description
Types: feat, fix, docs, style, refactor, perf, test, chore, ci
Scopes: telegram, email, line, core, config, docs
Examples:
feat(telegram): add inline keyboard support
fix(email): resolve SMTP timeout issue #123
docs: update installation instructions
```
### Branch Naming
```bash
feature/discord-integration # New feature
fix/issue-123 # Bug fix
docs/update-readme # Documentation
refactor/notification-system # Refactoring
```
**Note**: Detailed naming rules are shown directly in issue/PR templates when you create them on GitHub.
## 🔄 Workflow
### Before PR
1. Test all affected platforms
2. Run security checks: `grep -r "TOKEN\|SECRET\|PASS" --include="*.js" src/`
3. Ensure no console.log in production code
4. Update docs if API changes
### Commit Format
```bash
feat(telegram): add inline keyboard support
fix(email): resolve SMTP timeout issue #123
docs: update installation instructions
refactor(core): simplify notification logic
chore: update dependencies
```
## ✅ PR Checklist
- [ ] **No hardcoded values** (all config in .env or config files)
- [ ] **No secrets in code** (tokens, passwords, keys)
- [ ] **Input validation added** where needed
- [ ] **Error handling implemented** (try/catch blocks)
- [ ] **Tested locally** with tmux
- [ ] **Tested affected platforms** (Email/Telegram/LINE)
- [ ] **Code follows existing patterns**
- [ ] **Updated documentation** if needed
## 🚨 Important Rules
1. **Never commit .env files**
2. **Always validate external input**
3. **Keep platform code isolated** in `src/channels/`
4. **Follow existing patterns** - check similar code first
5. **Test with tmux** before submitting
## 📞 Get Help
- Issues: [GitHub Issues](https://github.com/JessyTsui/Claude-Code-Remote/issues)
- Twitter: [@Jiaxi_Cui](https://x.com/Jiaxi_Cui)

View File

@ -1,63 +0,0 @@
---
name: 🐛 Bug Report
about: Report something that is broken
title: '[BUG] '
labels: 'bug'
---
<!--
🏷️ ISSUE TITLE NAMING RULES:
Format: [BUG] Short clear description of the problem
✅ GOOD EXAMPLES:
- [BUG] Telegram bot not responding to commands
- [BUG] Email notifications fail with SMTP timeout error
- [BUG] LINE webhook returns 401 unauthorized
- [BUG] Desktop notifications not showing on macOS
- [BUG] Installation fails on Windows with Node 18
- [BUG] tmux session detection not working
- [BUG] Hook configuration file not found
❌ BAD EXAMPLES:
- Bug report (no [BUG] prefix)
- [BUG] It doesn't work (too vague)
- Telegram issue (no [BUG] prefix, not descriptive)
- [BUG] Problem (not descriptive enough)
📋 AVAILABLE ISSUE TYPES:
1. 🐛 Bug Report (this template) - Report broken functionality
2. ✨ Feature Request - Request new features
3. ❓ Question - Ask usage questions
4. 🔒 Security Report - Report security vulnerabilities
5. ⚡ Performance Issue - Report performance problems
6. 🔧 Enhancement - Suggest improvements to existing features
7. 💬 Discussion - General discussions and brainstorming
-->
## Bug Type (select one)
- [ ] Installation issue
- [ ] Platform not working (Email/Telegram/LINE)
- [ ] Notification not received
- [ ] Command injection failed
- [ ] Configuration error
## What happened?
<!-- Clear description -->
## Steps to reproduce
1.
2.
3.
## Expected behavior
<!-- What should happen? -->
## Environment
- **Node version**:
- **OS**:
- **Platform**: Email / Telegram / LINE / All
## Error logs
```
paste error here
```

View File

@ -1 +0,0 @@
blank_issues_enabled: false

View File

@ -1,67 +0,0 @@
---
name: 💬 Discussion
about: General discussions and brainstorming
title: '[DISCUSSION] '
labels: 'discussion'
---
<!--
🏷️ ISSUE TITLE NAMING RULES:
Format: [DISCUSSION] Short description of the discussion topic
✅ GOOD EXAMPLES:
- [DISCUSSION] Should we support WhatsApp integration?
- [DISCUSSION] Best practices for notification rate limiting
- [DISCUSSION] Architecture discussion for multi-tenant support
- [DISCUSSION] Ideas for improving user onboarding
- [DISCUSSION] Feedback on new configuration format
- [DISCUSSION] Community guidelines for contributors
- [DISCUSSION] Roadmap planning for next major version
❌ BAD EXAMPLES:
- Discussion (no [DISCUSSION] prefix)
- [DISCUSSION] Question (use [QUESTION] for specific questions)
- Ideas (no [DISCUSSION] prefix)
- [DISCUSSION] Help (use [QUESTION] for help requests)
📋 AVAILABLE ISSUE TYPES:
1. 🐛 Bug Report - Report broken functionality
2. ✨ Feature Request - Request new features
3. ❓ Question - Ask usage questions
4. 🔒 Security Report - Report security vulnerabilities
5. ⚡ Performance Issue - Report performance problems
6. 🔧 Enhancement - Suggest improvements to existing features
7. 💬 Discussion (this template) - General discussions and brainstorming
-->
## Discussion Topic
<!-- What would you like to discuss? -->
## Context/Background
<!-- Provide relevant background information -->
## Key Questions
<!-- What specific questions should we consider? -->
1.
2.
3.
## Potential Options/Ideas
<!-- What are some possible approaches or solutions? -->
- Option A:
- Option B:
- Option C:
## Impact/Considerations
<!-- What should we keep in mind? -->
- **Users**:
- **Development**:
- **Maintenance**:
- **Performance**:
## Looking for
- [ ] Community feedback and opinions
- [ ] Technical input from maintainers
- [ ] Ideas and suggestions
- [ ] Help with decision making
- [ ] Brainstorming session

View File

@ -1,61 +0,0 @@
---
name: 🔧 Enhancement
about: Suggest improvements to existing features
title: '[ENHANCEMENT] '
labels: 'enhancement'
---
<!--
🏷️ ISSUE TITLE NAMING RULES:
Format: [ENHANCEMENT] Short description of the enhancement
✅ GOOD EXAMPLES:
- [ENHANCEMENT] Improve error messages for failed notifications
- [ENHANCEMENT] Add configuration validation on startup
- [ENHANCEMENT] Better logging for debugging command execution
- [ENHANCEMENT] Improve Telegram bot command help text
- [ENHANCEMENT] Add retry mechanism for failed email sends
- [ENHANCEMENT] Better handling of network timeouts
- [ENHANCEMENT] Improve notification formatting options
❌ BAD EXAMPLES:
- Enhancement (no [ENHANCEMENT] prefix)
- [ENHANCEMENT] Better (not specific enough)
- Improve something (no [ENHANCEMENT] prefix)
- [ENHANCEMENT] Fix (use [BUG] for fixes)
📋 AVAILABLE ISSUE TYPES:
1. 🐛 Bug Report - Report broken functionality
2. ✨ Feature Request - Request new features
3. ❓ Question - Ask usage questions
4. 🔒 Security Report - Report security vulnerabilities
5. ⚡ Performance Issue - Report performance problems
6. 🔧 Enhancement (this template) - Suggest improvements to existing features
7. 💬 Discussion - General discussions and brainstorming
-->
## What feature needs enhancement?
<!-- Which existing feature should be improved? -->
## Current behavior
<!-- How does it work now? -->
## Suggested improvement
<!-- What should be enhanced and how? -->
## Why is this enhancement needed?
<!-- What problem does this solve? -->
## Priority
- [ ] Nice to have
- [ ] Would improve user experience
- [ ] Important for workflow
- [ ] Critical improvement needed
## Implementation suggestions (optional)
<!-- If you have ideas on how to implement this -->
## Environment
- **Node version**:
- **OS**:
- **Platform**: Email / Telegram / LINE / All

View File

@ -1,57 +0,0 @@
---
name: ✨ Feature Request
about: Suggest a new feature
title: '[FEATURE] '
labels: 'enhancement'
---
<!--
🏷️ ISSUE TITLE NAMING RULES:
Format: [FEATURE] Short clear description of the feature
✅ GOOD EXAMPLES:
- [FEATURE] Add Discord platform integration
- [FEATURE] Auto-retry failed notifications
- [FEATURE] Export command history to CSV
- [FEATURE] Add support for Slack webhooks
- [FEATURE] Implement notification scheduling
- [FEATURE] Add multi-language support
- [FEATURE] Command rate limiting
- [FEATURE] Notification templates customization
❌ BAD EXAMPLES:
- Feature request (no [FEATURE] prefix)
- [FEATURE] New feature (too vague)
- Discord support (no [FEATURE] prefix)
- [FEATURE] Improvement (not specific enough)
📋 AVAILABLE ISSUE TYPES:
1. 🐛 Bug Report - Report broken functionality
2. ✨ Feature Request (this template) - Request new features
3. ❓ Question - Ask usage questions
4. 🔒 Security Report - Report security vulnerabilities
5. ⚡ Performance Issue - Report performance problems
6. 🔧 Enhancement - Suggest improvements to existing features
7. 💬 Discussion - General discussions and brainstorming
-->
## Feature Type (select one)
- [ ] New platform integration (Discord/Slack/WhatsApp)
- [ ] Notification enhancement
- [ ] Command/control improvement
- [ ] Performance optimization
- [ ] Security enhancement
## What feature do you want?
<!-- Clear description -->
## Why do you need this?
<!-- What problem does it solve? -->
## How should it work?
<!-- Describe the solution -->
## Priority
- [ ] Nice to have
- [ ] Important
- [ ] Critical for my workflow

View File

@ -1,78 +0,0 @@
---
name: ⚡ Performance Issue
about: Report performance problems
title: '[PERFORMANCE] '
labels: 'performance'
---
<!--
🏷️ ISSUE TITLE NAMING RULES:
Format: [PERFORMANCE] Short description of the performance issue
✅ GOOD EXAMPLES:
- [PERFORMANCE] Email notifications take too long to send
- [PERFORMANCE] High memory usage during command execution
- [PERFORMANCE] Telegram bot response time over 5 seconds
- [PERFORMANCE] Application startup takes too long
- [PERFORMANCE] CPU usage spikes during notification processing
- [PERFORMANCE] Database queries running slowly
- [PERFORMANCE] Large file uploads causing timeout
❌ BAD EXAMPLES:
- Performance issue (no [PERFORMANCE] prefix)
- [PERFORMANCE] Slow (not specific enough)
- App is slow (no [PERFORMANCE] prefix)
- [PERFORMANCE] Problem (too vague)
📋 AVAILABLE ISSUE TYPES:
1. 🐛 Bug Report - Report broken functionality
2. ✨ Feature Request - Request new features
3. ❓ Question - Ask usage questions
4. 🔒 Security Report - Report security vulnerabilities
5. ⚡ Performance Issue (this template) - Report performance problems
6. 🔧 Enhancement - Suggest improvements to existing features
7. 💬 Discussion - General discussions and brainstorming
-->
## Performance Issue Type (select one)
- [ ] Slow response time
- [ ] High memory usage
- [ ] High CPU usage
- [ ] Long startup time
- [ ] Database performance
- [ ] Network latency
- [ ] File I/O performance
## Current behavior
<!-- What is happening now? Include metrics if available -->
## Expected performance
<!-- What should the performance be? -->
## When does this occur?
- [ ] Always
- [ ] During high load
- [ ] With specific commands
- [ ] With large files/data
- [ ] At startup
- [ ] Other:
## Performance metrics (if available)
- **Response time**:
- **Memory usage**:
- **CPU usage**:
- **Load time**:
## Steps to reproduce
1.
2.
3.
## Environment
- **Node version**:
- **OS**:
- **Platform**: Email / Telegram / LINE / All
- **System specs**:
## Additional context
<!-- Any other information about the performance issue -->

View File

@ -1,60 +0,0 @@
---
name: ❓ Question
about: Ask usage questions
title: '[QUESTION] '
labels: 'question'
---
<!--
🏷️ ISSUE TITLE NAMING RULES:
Format: [QUESTION] Short description of your question
✅ GOOD EXAMPLES:
- [QUESTION] How to configure Telegram bot with custom commands?
- [QUESTION] Can I use multiple email accounts for notifications?
- [QUESTION] How to set up LINE webhook for notifications?
- [QUESTION] What Node.js versions are supported?
- [QUESTION] How to troubleshoot failed email notifications?
- [QUESTION] Can notifications be scheduled for specific times?
- [QUESTION] How to configure environment variables on Windows?
❌ BAD EXAMPLES:
- Question (no [QUESTION] prefix)
- [QUESTION] Help (not specific enough)
- How to setup (no [QUESTION] prefix)
- [QUESTION] Problem (use [BUG] for problems)
📋 AVAILABLE ISSUE TYPES:
1. 🐛 Bug Report - Report broken functionality
2. ✨ Feature Request - Request new features
3. ❓ Question (this template) - Ask usage questions
4. 🔒 Security Report - Report security vulnerabilities
5. ⚡ Performance Issue - Report performance problems
6. 🔧 Enhancement - Suggest improvements to existing features
7. 💬 Discussion - General discussions and brainstorming
-->
## Question Category (select one)
- [ ] Installation and setup
- [ ] Configuration
- [ ] Platform integration (Email/Telegram/LINE)
- [ ] Command usage
- [ ] Troubleshooting
- [ ] Best practices
## Your Question
<!-- What do you want to know? -->
## What have you tried?
<!-- What steps have you already taken? -->
## Context
<!-- Provide relevant details about your setup -->
## Environment (if relevant)
- **Node version**:
- **OS**:
- **Platform**: Email / Telegram / LINE / All
## Additional Information
<!-- Any other relevant information -->

View File

@ -1,69 +0,0 @@
---
name: 🔒 Security Report
about: Report a security vulnerability
title: '[SECURITY] '
labels: 'security'
---
<!--
🏷️ ISSUE TITLE NAMING RULES:
Format: [SECURITY] Short description of the security issue
✅ GOOD EXAMPLES:
- [SECURITY] Hardcoded credentials in config file
- [SECURITY] Command injection vulnerability in email handler
- [SECURITY] Exposed API keys in environment variables
- [SECURITY] Unauthorized access to notification settings
- [SECURITY] XSS vulnerability in notification content
- [SECURITY] Path traversal in file upload feature
- [SECURITY] SQL injection in database queries
❌ BAD EXAMPLES:
- Security issue (no [SECURITY] prefix)
- [SECURITY] Problem (not descriptive enough)
- Vulnerability (no [SECURITY] prefix)
- [SECURITY] Bug (too vague)
📋 AVAILABLE ISSUE TYPES:
1. 🐛 Bug Report - Report broken functionality
2. ✨ Feature Request - Request new features
3. ❓ Question - Ask usage questions
4. 🔒 Security Report (this template) - Report security vulnerabilities
5. ⚡ Performance Issue - Report performance problems
6. 🔧 Enhancement - Suggest improvements to existing features
7. 💬 Discussion - General discussions and brainstorming
-->
## Severity Level (select one)
- [ ] 🔴 Critical - Immediate action required
- [ ] 🟠 High - Should be fixed soon
- [ ] 🟡 Medium - Should be addressed
- [ ] 🟢 Low - Minor security concern
## Vulnerability Type (select one)
- [ ] Authentication/Authorization
- [ ] Code injection (Command/SQL/XSS)
- [ ] Data exposure/leak
- [ ] Hardcoded secrets/credentials
- [ ] Input validation
- [ ] Path traversal
- [ ] Other
## Description
<!-- Clear description of the security issue -->
## Steps to reproduce
1.
2.
3.
## Impact
<!-- What could an attacker achieve? -->
## Suggested fix
<!-- If you have suggestions for fixing this -->
## Environment
- **Node version**:
- **OS**:
- **Platform**: Email / Telegram / LINE / All

View File

@ -1,80 +0,0 @@
<!--
🏷️ PR TITLE NAMING RULES:
Format: type(scope): description
📋 AVAILABLE PR TYPES (choose one for title):
- feat: New feature or enhancement
- fix: Bug fix
- docs: Documentation only changes
- style: Code formatting, whitespace, semicolons
- refactor: Code refactoring (no functionality change)
- perf: Performance improvements
- test: Adding or updating tests
- chore: Maintenance, dependencies, build tools
- ci: CI/CD configuration changes
📍 SCOPES (optional but recommended):
- telegram: Telegram platform
- email: Email platform
- line: LINE platform
- discord: Discord platform
- core: Core functionality
- config: Configuration files
- docs: Documentation
✅ GOOD PR TITLE EXAMPLES:
- feat(telegram): add inline keyboard support
- fix(email): resolve SMTP timeout issue #123
- feat(discord): add Discord platform integration
- docs: update installation instructions
- refactor(core): simplify notification logic
- perf(telegram): optimize message sending speed
- fix(line): handle webhook authentication error
- chore: update dependencies to latest versions
- style(core): fix code formatting and indentation
- test(email): add unit tests for SMTP connection
- ci: add automated security scanning
❌ BAD PR TITLE EXAMPLES:
- Add feature (no type, no scope)
- Fix bug (too vague, no scope)
- Update code (not descriptive)
- telegram fix (wrong format)
- New Discord support (missing type prefix)
-->
## PR Type (REQUIRED: select at least one)
- [ ] 🐛 **fix**: Bug fix (non-breaking change)
- [ ] ✨ **feat**: New feature (non-breaking change)
- [ ] 🔌 **feat**: Platform integration (new platform support)
- [ ] 💥 **feat**: Breaking change (changes existing functionality)
- [ ] 📚 **docs**: Documentation only changes
- [ ] ♻️ **refactor**: Code refactoring (no functionality change)
- [ ] ⚡ **perf**: Performance improvements
- [ ] 🎨 **style**: Code formatting, whitespace, semicolons
- [ ] 🔧 **chore**: Maintenance, dependencies, build tools
- [ ] 🚨 **test**: Adding or updating tests
- [ ] 🔄 **ci**: CI/CD configuration changes
## What does this PR do?
<!-- Clear description -->
## Related Issue
<!-- Fixes #123 or Closes #123 -->
## Code Quality Checklist (ALL REQUIRED)
- [ ] **No hardcoded secrets** (use process.env.* or config files)
- [ ] **No console.log** in production code (use logger.*)
- [ ] **Error handling** implemented (try/catch blocks)
- [ ] **Input validation** where needed
- [ ] **Tested locally** with tmux
## Platform Testing
- [ ] Email
- [ ] Telegram
- [ ] LINE
- [ ] Desktop notifications
## How did you test this?
1.
2.

View File

@ -1,48 +0,0 @@
name: CI
on:
push:
branches: [ master, main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Security audit
run: npm audit --audit-level=moderate || true
- name: Validate JSON configs
run: |
echo "🔍 Validating JSON configuration files..."
for file in $(find . -name "*.json" -not -path "./node_modules/*" -not -path "./.git/*"); do
echo "Checking $file"
if ! python3 -m json.tool "$file" > /dev/null 2>&1; then
echo "❌ Invalid JSON: $file"
exit 1
fi
done
echo "✅ All JSON files are valid"
- name: Check tmux availability
run: |
if command -v tmux &> /dev/null; then
echo "✅ tmux is available: $(tmux -V)"
else
echo "Installing tmux..."
sudo apt-get update && sudo apt-get install -y tmux
fi

View File

@ -1,95 +0,0 @@
name: Project Management
on:
issues:
types: [opened, edited, labeled]
schedule:
- cron: '0 0 * * 0' # Weekly cleanup on Sunday
jobs:
# Auto-label issues
auto-label:
runs-on: ubuntu-latest
if: github.event_name == 'issues'
permissions:
issues: write
steps:
- name: Auto-label based on content
uses: actions/github-script@v7
with:
script: |
const item = context.payload.issue;
const body = (item.body || '').toLowerCase();
const title = (item.title || '').toLowerCase();
const labels = [];
// Platform labels
if (title.includes('telegram') || body.includes('telegram')) labels.push('platform:telegram');
if (title.includes('email') || body.includes('email')) labels.push('platform:email');
if (title.includes('line') || body.includes('line')) labels.push('platform:line');
if (title.includes('discord') || body.includes('discord')) labels.push('platform:discord');
// Priority labels
if (title.includes('critical') || body.includes('critical')) labels.push('priority:high');
if (title.includes('urgent') || body.includes('urgent')) labels.push('priority:high');
// Type labels
if (title.includes('[bug]')) labels.push('type:bug');
if (title.includes('[feature]')) labels.push('type:enhancement');
if (title.includes('[question]')) labels.push('type:question');
if (title.includes('[security]')) labels.push('type:security');
if (title.includes('[performance]')) labels.push('type:performance');
if (title.includes('[enhancement]')) labels.push('type:enhancement');
if (title.includes('[discussion]')) labels.push('type:discussion');
if (labels.length > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: item.number,
labels: labels
});
}
# Weekly maintenance
weekly-cleanup:
runs-on: ubuntu-latest
if: github.event_name == 'schedule'
permissions:
issues: write
steps:
- name: Close stale issues
uses: actions/github-script@v7
with:
script: |
const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'question',
sort: 'updated',
direction: 'asc',
per_page: 100
});
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
for (const issue of issues) {
if (new Date(issue.updated_at) < thirtyDaysAgo) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: 'This question has been inactive for 30 days and will be closed. Feel free to reopen if you still need help.'
});
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
state: 'closed'
});
}
}

View File

@ -25,8 +25,8 @@ Control [Claude Code](https://claude.ai/code) remotely via multiple messaging pl
## ✨ Features ## ✨ Features
- **📧 Multiple Messaging Platforms**: - **📧 Multiple Messaging Platforms**:
- Email notifications with full execution trace and reply-to-send commands ![](./assets/email_demo.png) - Email notifications with full execution trace and reply-to-send commands
- Telegram Bot with interactive buttons and slash commands ![](./assets/telegram_demo.png) - Telegram Bot with interactive buttons and slash commands ✅ **NEW**
- LINE messaging with token-based commands - LINE messaging with token-based commands
- Desktop notifications with sound alerts - Desktop notifications with sound alerts
- **🔄 Two-way Control**: Reply to messages or emails to send new commands - **🔄 Two-way Control**: Reply to messages or emails to send new commands
@ -42,13 +42,13 @@ Control [Claude Code](https://claude.ai/code) remotely via multiple messaging pl
## 📅 Changelog ## 📅 Changelog
### August 2025 ### August 2025
- **2025-08-02**: Add full execution trace to email notifications ([#14](https://github.com/JessyTsui/Claude-Code-Remote/pull/14) by [@vaclisinc](https://github.com/vaclisinc)) - **2025-08-02**: Add full execution trace to email notifications ([#14](https://github.com/JessyTsui/Claude-Code-Remote/pull/14))
- **2025-08-01**: Enhanced Multi-Channel Notification System ([#1](https://github.com/JessyTsui/Claude-Code-Remote/pull/1) by [@laihenyi](https://github.com/laihenyi) [@JessyTsui](https://github.com/JessyTsui)) - **2025-08-01**: Enhanced Multi-Channel Notification System (by @laihenyi @JessyTsui)
- ✅ **Telegram Integration Completed** - Interactive buttons, real-time commands, smart personal/group chat handling - ✅ **Telegram Integration Completed** - Interactive buttons, real-time commands, smart personal/group chat handling
- ✅ **Multi-Channel Notifications** - Simultaneous delivery to Desktop, Telegram, Email, LINE - ✅ **Multi-Channel Notifications** - Simultaneous delivery to Desktop, Telegram, Email, LINE
- ✅ **Smart Sound Alerts** - Always-on audio feedback with customizable sounds - ✅ **Smart Sound Alerts** - Always-on audio feedback with customizable sounds
- ✅ **Intelligent Session Management** - Auto-detection, real conversation content, 24-hour tokens - ✅ **Intelligent Session Management** - Auto-detection, real conversation content, 24-hour tokens
- **2025-08-01**: Fix #9 #12: Add configuration to disable subagent notifications ([#10](https://github.com/JessyTsui/Claude-Code-Remote/pull/10) by [@vaclisinc](https://github.com/vaclisinc)) - **2025-08-01**: Fix #9 #12: Add configuration to disable subagent notifications ([#10](https://github.com/JessyTsui/Claude-Code-Remote/pull/10))
- **2025-08-01**: Implement terminal-style UI for email notifications ([#8](https://github.com/JessyTsui/Claude-Code-Remote/pull/8) by [@vaclisinc](https://github.com/vaclisinc)) - **2025-08-01**: Implement terminal-style UI for email notifications ([#8](https://github.com/JessyTsui/Claude-Code-Remote/pull/8) by [@vaclisinc](https://github.com/vaclisinc))
- **2025-08-01**: Fix working directory issue - enable claude-remote to run from any directory ([#7](https://github.com/JessyTsui/Claude-Code-Remote/pull/7) by [@vaclisinc](https://github.com/vaclisinc)) - **2025-08-01**: Fix working directory issue - enable claude-remote to run from any directory ([#7](https://github.com/JessyTsui/Claude-Code-Remote/pull/7) by [@vaclisinc](https://github.com/vaclisinc))
@ -141,22 +141,6 @@ TELEGRAM_WEBHOOK_URL=https://your-ngrok-url.app
SESSION_MAP_PATH=/your/path/to/Claude-Code-Remote/src/data/session-map.json SESSION_MAP_PATH=/your/path/to/Claude-Code-Remote/src/data/session-map.json
``` ```
**Optional Telegram settings:**
```env
# Force IPv4 connections to Telegram API (default: false)
# Enable this if you experience connectivity issues with IPv6
TELEGRAM_FORCE_IPV4=true
```
**Network Configuration Notes:**
- **IPv4 vs IPv6**: Some network environments may have unstable IPv6 connectivity to Telegram's API servers
- **When to use `TELEGRAM_FORCE_IPV4=true`**:
- Connection timeouts or failures when sending messages
- Inconsistent webhook delivery
- Network environments that don't properly support IPv6
- **Default behavior**: Uses system default (usually IPv6 when available, fallback to IPv4)
- **Performance impact**: Minimal - only affects initial connection establishment
#### Option C: Configure LINE #### Option C: Configure LINE
**Required LINE settings:** **Required LINE settings:**
@ -204,26 +188,7 @@ export CLAUDE_HOOKS_CONFIG=/your/path/to/Claude-Code-Remote/claude-hooks.json
> **Note**: Subagent notifications are disabled by default. To enable them, set `enableSubagentNotifications: true` in your config. See [Subagent Notifications Guide](./docs/SUBAGENT_NOTIFICATIONS.md) for details. > **Note**: Subagent notifications are disabled by default. To enable them, set `enableSubagentNotifications: true` in your config. See [Subagent Notifications Guide](./docs/SUBAGENT_NOTIFICATIONS.md) for details.
### 5. Start tmux Session with Claude Code ### 5. Start Services
**IMPORTANT**: Claude Code Remote requires Claude to run in a tmux session for command injection to work.
```bash
# Start a new tmux session
tmux new-session -d -s claude-session
# Attach to the session
tmux attach-session -t claude-session
# Inside tmux, start Claude Code with hooks enabled
claude-code --config /path/to/your/claude/settings.json
# Detach from tmux (Ctrl+B, then D) to leave Claude running in background
```
> **Note**: Make sure your `~/.claude/settings.json` or project-specific config includes the hooks configuration from Step 4.
### 6. Start Services
#### For All Platforms (Recommended) #### For All Platforms (Recommended)
```bash ```bash
@ -256,7 +221,7 @@ npm run line
node start-line-webhook.js node start-line-webhook.js
``` ```
### 7. Test Your Setup ### 6. Test Your Setup
**Quick Test:** **Quick Test:**
```bash ```bash

0
claude-hook-notify.js Normal file → Executable file
View File

View File

@ -6,7 +6,7 @@
"hooks": [ "hooks": [
{ {
"type": "command", "type": "command",
"command": "node /Users/path/to/Claude-Code-Remote/claude-hook-notify.js completed", "command": "node /Users/jessytsui/dev/Claude-Code-Remote/claude-hook-notify.js completed",
"timeout": 5 "timeout": 5
} }
] ]
@ -18,7 +18,7 @@
"hooks": [ "hooks": [
{ {
"type": "command", "type": "command",
"command": "node /Users/path/to/Claude-Code-Remote/claude-hook-notify.js waiting", "command": "node /Users/jessytsui/dev/Claude-Code-Remote/claude-hook-notify.js waiting",
"timeout": 5 "timeout": 5
} }
] ]

0
claude-remote.js Normal file → Executable file
View File

View File

@ -6,32 +6,7 @@
}, },
"email": { "email": {
"type": "email", "type": "email",
"enabled": true, "enabled": true
"config": {
"smtp": {
"host": "smtp.gmail.com",
"port": 465,
"secure": true,
"auth": {
"user": "2322176165lsa@gmail.com",
"pass": "frkxntpirpfrhqre"
}
},
"imap": {
"host": "imap.gmail.com",
"port": 993,
"secure": true,
"auth": {
"user": "2322176165lsa@gmail.com",
"pass": "frkxntpirpfrhqre"
}
},
"from": "2322176165lsa@gmail.com",
"to": "2322176165@qq.com",
"template": {
"checkInterval": 20
}
}
}, },
"discord": { "discord": {
"type": "chat", "type": "chat",
@ -49,7 +24,6 @@
"botToken": "", "botToken": "",
"chatId": "", "chatId": "",
"groupId": "", "groupId": "",
"forceIPv4": false,
"whitelist": [] "whitelist": []
} }
}, },

0
fix-telegram.sh Normal file → Executable file
View File

0
setup-telegram.sh Normal file → Executable file
View File

View File

@ -20,7 +20,7 @@ const TelegramChannel = require('./src/channels/telegram/telegram');
class SmartMonitor { class SmartMonitor {
constructor() { constructor() {
this.sessionName = process.env.TMUX_SESSION || 'claude-session'; this.sessionName = process.env.TMUX_SESSION || 'claude-real';
this.lastOutput = ''; this.lastOutput = '';
this.processedResponses = new Set(); // 記錄已處理的回應 this.processedResponses = new Set(); // 記錄已處理的回應
this.checkInterval = 1000; // Check every 1 second this.checkInterval = 1000; // Check every 1 second

View File

@ -41,18 +41,6 @@ class TelegramChannel extends NotificationChannel {
return true; return true;
} }
/**
* Generate network options for axios requests
* @returns {Object} Network options object
*/
_getNetworkOptions() {
const options = {};
if (this.config.forceIPv4) {
options.family = 4;
}
return options;
}
_generateToken() { _generateToken() {
// Generate short Token (uppercase letters + numbers, 8 digits) // Generate short Token (uppercase letters + numbers, 8 digits)
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
@ -78,105 +66,6 @@ class TelegramChannel extends NotificationChannel {
} }
} }
/**
* Escape special characters for Telegram Markdown
* @param {string} text - Text to escape
* @returns {string} - Escaped text
*/
_escapeMarkdown(text) {
if (!text) return '';
// Minimal escaping to avoid message rejection
// Over-escaping causes Telegram to reject the message
return text
.replace(/\*/g, '\\*') // Escape asterisks
.replace(/_/g, '\\_') // Escape underscores
.replace(/\[/g, '\\[') // Escape square brackets
.replace(/\]/g, '\\]') // Escape square brackets
.replace(/`/g, '\\`'); // Escape backticks
}
/**
* Create a safe plain text version without markdown formatting
* @param {string} text - Text to make safe
* @returns {string} - Safe text
*/
_createSafeText(text) {
if (!text) return '';
// Remove problematic characters entirely to ensure message sends
return text
.replace(/[_*\[\]()~`>#+=|{}.!\\-]/g, '') // Remove special chars
.replace(/\s+/g, ' ') // Collapse multiple spaces
.trim();
}
/**
* Calculate message length including formatting
* @param {string} message - Message to calculate
* @returns {number} - Message length
*/
_calculateMessageLength(message) {
return message.length;
}
/**
* Split long text into chunks that fit Telegram limits
* @param {string} text - Text to split
* @param {number} maxLength - Maximum length per chunk
* @returns {string[]} - Array of text chunks
*/
_splitTextIntoChunks(text, maxLength = 3000) {
if (text.length <= maxLength) {
return [text];
}
const chunks = [];
let currentChunk = '';
const lines = text.split('\n');
for (const line of lines) {
// If adding this line would exceed the limit
if (currentChunk.length + line.length + 1 > maxLength) {
if (currentChunk) {
chunks.push(currentChunk.trim());
currentChunk = '';
}
// If single line is too long, split it by words
if (line.length > maxLength) {
const words = line.split(' ');
let wordChunk = '';
for (const word of words) {
if (wordChunk.length + word.length + 1 > maxLength) {
if (wordChunk) {
chunks.push(wordChunk.trim());
wordChunk = word;
} else {
// Single word is too long, truncate it
chunks.push(word.substring(0, maxLength - 3) + '...');
}
} else {
wordChunk += (wordChunk ? ' ' : '') + word;
}
}
if (wordChunk) {
currentChunk = wordChunk;
}
} else {
currentChunk = line;
}
} else {
currentChunk += (currentChunk ? '\n' : '') + line;
}
}
if (currentChunk) {
chunks.push(currentChunk.trim());
}
return chunks;
}
async _getBotUsername() { async _getBotUsername() {
if (this.botUsername) { if (this.botUsername) {
return this.botUsername; return this.botUsername;
@ -184,8 +73,7 @@ class TelegramChannel extends NotificationChannel {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.apiBaseUrl}/bot${this.config.botToken}/getMe`, `${this.apiBaseUrl}/bot${this.config.botToken}/getMe`
this._getNetworkOptions()
); );
if (response.data.ok && response.data.result.username) { if (response.data.ok && response.data.result.username) {
@ -254,118 +142,16 @@ class TelegramChannel extends NotificationChannel {
} }
}; };
// Check if we need to use multi-message sending based on original response length
const fullResponse = notification.metadata?.claudeResponse || '';
const shouldUseMultiMessage = fullResponse.length > 1500; // Lower threshold for better experience
if (shouldUseMultiMessage) {
console.log(`[DEBUG] Using multi-message sending (original response: ${fullResponse.length} chars)`);
const success = await this._sendMultipleMessages(notification, sessionId, token, chatId, buttons);
if (!success) {
// Clean up failed session
await this._removeSession(sessionId);
}
return success;
}
// Use single message sending
console.log(`[DEBUG] Using single message sending (${messageText.length} chars)`);
try { try {
// Log the message details for debugging
console.log(`[DEBUG] =====================================================`);
console.log(`[DEBUG] Sending Telegram message, length: ${messageText.length}`);
console.log(`[DEBUG] Chat ID: ${chatId}`);
console.log(`[DEBUG] Session ID: ${sessionId}`);
console.log(`[DEBUG] Message preview:`, messageText.substring(0, 200) + '...');
console.log(`[DEBUG] =====================================================`);
const response = await axios.post( const response = await axios.post(
`${this.apiBaseUrl}/bot${this.config.botToken}/sendMessage`, `${this.apiBaseUrl}/bot${this.config.botToken}/sendMessage`,
requestData, requestData
{
...this._getNetworkOptions(),
timeout: 15000 // 15 second timeout
}
); );
console.log(`[DEBUG] ✅ Telegram message sent successfully!`);
this.logger.info(`Telegram message sent successfully, Session: ${sessionId}`); this.logger.info(`Telegram message sent successfully, Session: ${sessionId}`);
return true; return true;
} catch (error) { } catch (error) {
// Enhanced error logging this.logger.error('Failed to send Telegram message:', error.response?.data || error.message);
const errorData = error.response?.data;
const errorMessage = errorData?.description || error.message;
const errorCode = errorData?.error_code;
console.log(`[DEBUG] ❌ Telegram send error occurred:`);
console.log(`[DEBUG] Error Code: ${errorCode}`);
console.log(`[DEBUG] Error Message: ${errorMessage}`);
console.log(`[DEBUG] Full error response:`, JSON.stringify(errorData, null, 2));
console.log(`[DEBUG] Original message length: ${messageText.length}`);
this.logger.error(`Failed to send Telegram message (${errorCode}): ${errorMessage}`);
// Try multiple fallback strategies
console.log(`[DEBUG] Attempting fallback strategies...`);
// Strategy 1: Try without parse_mode (plain text)
try {
console.log(`[DEBUG] Trying Strategy 1: Plain text without markdown`);
const plainTextMessage = this._generateMinimalMessage(notification, token,
notification.type === 'completed' ? '✅' : '⏳',
notification.type === 'completed' ? 'Completed' : 'Waiting');
await axios.post(
`${this.apiBaseUrl}/bot${this.config.botToken}/sendMessage`,
{
chat_id: chatId,
text: plainTextMessage,
reply_markup: {
inline_keyboard: buttons
}
},
{
...this._getNetworkOptions(),
timeout: 15000
}
);
console.log(`[DEBUG] ✅ Strategy 1 succeeded: Plain text message sent`);
this.logger.info(`Telegram plain text fallback message sent successfully, Session: ${sessionId}`);
return true;
} catch (fallbackError1) {
console.log(`[DEBUG] ❌ Strategy 1 failed:`, fallbackError1.response?.data?.description || fallbackError1.message);
// Strategy 2: Try absolute minimal message without buttons
try {
console.log(`[DEBUG] Trying Strategy 2: Minimal message without buttons`);
const minimalMessage = `Claude Task Ready\\nToken: ${token}`;
await axios.post(
`${this.apiBaseUrl}/bot${this.config.botToken}/sendMessage`,
{
chat_id: chatId,
text: minimalMessage
},
{
...this._getNetworkOptions(),
timeout: 15000
}
);
console.log(`[DEBUG] ✅ Strategy 2 succeeded: Minimal message sent`);
this.logger.info(`Telegram minimal fallback message sent successfully, Session: ${sessionId}`);
return true;
} catch (fallbackError2) {
console.log(`[DEBUG] ❌ Strategy 2 failed:`, fallbackError2.response?.data?.description || fallbackError2.message);
console.log(`[DEBUG] ❌ All fallback strategies failed`);
this.logger.error('All Telegram fallback strategies failed');
}
}
// Clean up failed session // Clean up failed session
await this._removeSession(sessionId); await this._removeSession(sessionId);
return false; return false;
@ -377,315 +163,35 @@ class TelegramChannel extends NotificationChannel {
const emoji = type === 'completed' ? '✅' : '⏳'; const emoji = type === 'completed' ? '✅' : '⏳';
const status = type === 'completed' ? 'Completed' : 'Waiting for Input'; const status = type === 'completed' ? 'Completed' : 'Waiting for Input';
try {
// Method 1: Try with minimal markdown formatting
let messageText = this._generateFormattedMessage(notification, token, emoji, status);
if (messageText.length <= 4000) {
console.log(`[DEBUG] Generated formatted message length: ${messageText.length}`);
return messageText;
}
// Method 2: If too long, try plain text version
console.log(`[DEBUG] Formatted message too long (${messageText.length}), trying plain text`);
messageText = this._generatePlainTextMessage(notification, token, emoji, status);
if (messageText.length <= 4000) {
console.log(`[DEBUG] Generated plain text message length: ${messageText.length}`);
return messageText;
}
// Method 3: If still too long, use minimal fallback
console.log(`[DEBUG] Plain text still too long (${messageText.length}), using minimal fallback`);
return this._generateMinimalMessage(notification, token, emoji, status);
} catch (error) {
console.log(`[DEBUG] Error generating message: ${error.message}, using safe fallback`);
return this._generateMinimalMessage(notification, token, emoji, status);
}
}
_generateFormattedMessage(notification, token, emoji, status) {
let messageText = `${emoji} *Claude Task ${status}*\n`; let messageText = `${emoji} *Claude Task ${status}*\n`;
messageText += `*Project:* ${notification.project || 'Unknown'}\n`; messageText += `*Project:* ${notification.project}\n`;
messageText += `*Token:* \`${token}\`\n\n`; messageText += `*Session Token:* \`${token}\`\n\n`;
// Add user question if available (limited length) if (notification.metadata) {
if (notification.metadata?.userQuestion) { if (notification.metadata.userQuestion) {
const question = notification.metadata.userQuestion.substring(0, 150); messageText += `📝 *Your Question:*\n${notification.metadata.userQuestion.substring(0, 200)}`;
messageText += `📝 *Question:* ${this._escapeMarkdown(question)}${question.length < notification.metadata.userQuestion.length ? '...' : ''}\n\n`; if (notification.metadata.userQuestion.length > 200) {
} messageText += '...';
}
// Add Claude response if available (limited length) messageText += '\n\n';
if (notification.metadata?.claudeResponse) {
const maxResponseLength = 3000 - messageText.length - 200; // Reserve space for instructions
let response = notification.metadata.claudeResponse;
if (response.length > maxResponseLength) {
response = response.substring(0, maxResponseLength - 10) + '...';
} }
messageText += `🤖 *Response:*\n${this._escapeMarkdown(response)}\n\n`; if (notification.metadata.claudeResponse) {
messageText += `🤖 *Claude Response:*\n${notification.metadata.claudeResponse.substring(0, 300)}`;
if (notification.metadata.claudeResponse.length > 300) {
messageText += '...';
}
messageText += '\n\n';
}
} }
messageText += `💬 Use: \`/cmd ${token} <command>\``; messageText += `💬 *To send a new command:*\n`;
messageText += `Reply with: \`/cmd ${token} <your command>\`\n`;
messageText += `Example: \`/cmd ${token} Please analyze this code\``;
return messageText; return messageText;
} }
_generatePlainTextMessage(notification, token, emoji, status) {
let messageText = `${emoji} Claude Task ${status}\n`;
messageText += `Project: ${notification.project || 'Unknown'}\n`;
messageText += `Token: ${token}\n\n`;
// Add user question (plain text, limited)
if (notification.metadata?.userQuestion) {
const question = this._createSafeText(notification.metadata.userQuestion.substring(0, 150));
messageText += `Question: ${question}${question.length < notification.metadata.userQuestion.length ? '...' : ''}\n\n`;
}
// Add Claude response (plain text, limited)
if (notification.metadata?.claudeResponse) {
const maxResponseLength = 3000 - messageText.length - 100;
let response = this._createSafeText(notification.metadata.claudeResponse);
if (response.length > maxResponseLength) {
response = response.substring(0, maxResponseLength - 10) + '...';
}
messageText += `Response: ${response}\n\n`;
}
messageText += `Use: /cmd ${token} <command>`;
return messageText;
}
_generateMinimalMessage(notification, token, emoji, status) {
return `${emoji} Claude Task ${status}\nToken: ${token}\nUse: /cmd ${token} <command>`;
}
/**
* Send multiple messages for long responses
* @param {Object} notification - Notification data
* @param {string} sessionId - Session ID
* @param {string} token - Session token
* @param {string} chatId - Telegram chat ID
* @param {Array} buttons - Inline keyboard buttons
* @returns {Promise<boolean>} - Success status
*/
async _sendMultipleMessages(notification, sessionId, token, chatId, buttons) {
const type = notification.type;
const emoji = type === 'completed' ? '✅' : '⏳';
const status = type === 'completed' ? 'Completed' : 'Waiting for Input';
try {
const fullResponse = notification.metadata?.claudeResponse || '';
const userQuestion = notification.metadata?.userQuestion || '';
console.log(`[DEBUG] Multi-message sending: Response length ${fullResponse.length} chars`);
// Split the response content into optimal chunks
const responseChunks = this._splitTextIntoChunks(fullResponse, 3200); // Slightly smaller for better formatting
const totalParts = Math.max(1, responseChunks.length);
console.log(`[DEBUG] Split response into ${totalParts} parts`);
// Create the first message with basic info
let firstMessage = `${emoji} *Claude Task ${status}* \\[1/${totalParts}\\]\n`;
firstMessage += `*Project:* ${this._escapeMarkdown(notification.project || 'Unknown')}\n`;
firstMessage += `*Token:* \`${token}\`\n\n`;
if (userQuestion) {
const question = userQuestion.substring(0, 150);
firstMessage += `📝 *Question:* ${this._escapeMarkdown(question)}${question.length < userQuestion.length ? '...' : ''}\n\n`;
}
// Add response header and first part
firstMessage += `🤖 *Claude Response:*\n`;
// Calculate space for first response part
const commandInstructions = `\n\n💬 *Commands:* \`/cmd ${token} <your command>\``;
const baseLength = firstMessage.length + commandInstructions.length;
const maxFirstResponseLength = 3800 - baseLength;
// Add first part of response
let firstResponsePart = '';
if (responseChunks.length > 0) {
firstResponsePart = responseChunks[0];
if (firstResponsePart.length > maxFirstResponseLength) {
// Further truncate first part if needed
firstResponsePart = firstResponsePart.substring(0, maxFirstResponseLength - 50) + '...';
}
}
firstMessage += this._escapeMarkdown(firstResponsePart);
// Add continuation indicator
if (totalParts > 1) {
firstMessage += `\n\n*\\[Continued in next message\\]*`;
}
firstMessage += commandInstructions;
// Send the first message
console.log(`[DEBUG] Sending first message (${firstMessage.length} chars)`);
const firstMessageResult = await this._sendSingleMessage(chatId, firstMessage, buttons, sessionId, 'Markdown');
if (!firstMessageResult.success) {
console.log(`[DEBUG] Failed to send first message, trying fallback`);
// Try plain text fallback for first message
const plainFirstMessage = firstMessage
.replace(/\*([^*]+)\*/g, '$1') // Remove bold
.replace(/\`([^`]+)\`/g, '$1') // Remove code
.replace(/\\(.)/g, '$1'); // Remove escapes
const fallbackResult = await this._sendSingleMessage(chatId, plainFirstMessage, buttons, sessionId, null);
if (!fallbackResult.success) {
return false;
}
}
const firstMessageId = firstMessageResult.messageId;
let successfulParts = 1;
// Send remaining parts
for (let i = 1; i < responseChunks.length; i++) {
const partNumber = i + 1;
let continuationMessage = `*\\[Part ${partNumber}/${totalParts}\\]*\n\n`;
continuationMessage += this._escapeMarkdown(responseChunks[i]);
console.log(`[DEBUG] Sending part ${partNumber}/${totalParts} (${continuationMessage.length} chars)`);
const success = await this._sendSingleMessage(
chatId,
continuationMessage,
null, // No buttons for continuation messages
sessionId,
'Markdown',
firstMessageId // Reply to first message to create thread
);
if (success.success) {
successfulParts++;
} else {
console.log(`[DEBUG] Failed to send part ${partNumber}/${totalParts}, trying plain text`);
// Try plain text fallback
const plainMessage = continuationMessage.replace(/\*([^*]+)\*/g, '$1').replace(/\\(.)/g, '$1');
const fallbackSuccess = await this._sendSingleMessage(
chatId, plainMessage, null, sessionId, null, firstMessageId
);
if (fallbackSuccess.success) {
successfulParts++;
}
}
// Add delay to prevent rate limiting
await new Promise(resolve => setTimeout(resolve, 800));
}
console.log(`[DEBUG] Multi-message sending completed: ${successfulParts}/${totalParts} parts sent successfully`);
// Send a final summary if not all parts were sent
if (successfulParts < totalParts) {
const summaryMessage = `⚠️ *Message Status:* ${successfulParts}/${totalParts} parts sent successfully\n\nSome parts may have been skipped due to formatting issues\\.`;
await this._sendSingleMessage(chatId, summaryMessage, null, sessionId, 'Markdown', firstMessageId);
}
return successfulParts > 0; // Success if at least first part was sent
} catch (error) {
console.log(`[DEBUG] Error in _sendMultipleMessages: ${error.message}`);
console.log(`[DEBUG] Stack trace:`, error.stack);
this.logger.error('Failed to send multiple messages:', error.message);
return false;
}
}
/**
* Send a single message with enhanced error handling
* @param {string} chatId - Telegram chat ID
* @param {string} messageText - Message text
* @param {Array} buttons - Inline keyboard buttons (optional)
* @param {string} sessionId - Session ID for logging
* @param {string} parseMode - Parse mode (optional)
* @param {number} replyToMessageId - Reply to message ID (optional)
* @returns {Promise<Object>} - {success: boolean, messageId: number}
*/
async _sendSingleMessage(chatId, messageText, buttons = null, sessionId = 'unknown', parseMode = null, replyToMessageId = null) {
const requestData = {
chat_id: chatId,
text: messageText
};
if (parseMode) {
requestData.parse_mode = parseMode;
}
if (buttons) {
requestData.reply_markup = {
inline_keyboard: buttons
};
}
if (replyToMessageId) {
requestData.reply_to_message_id = replyToMessageId;
}
try {
console.log(`[DEBUG] Sending message (${messageText.length} chars) to chat ${chatId}`);
const response = await axios.post(
`${this.apiBaseUrl}/bot${this.config.botToken}/sendMessage`,
requestData,
{
...this._getNetworkOptions(),
timeout: 15000
}
);
const messageId = response.data.result.message_id;
console.log(`[DEBUG] ✅ Message sent successfully, ID: ${messageId}`);
return { success: true, messageId: messageId };
} catch (error) {
const errorData = error.response?.data;
const errorMessage = errorData?.description || error.message;
const errorCode = errorData?.error_code;
console.log(`[DEBUG] ❌ Failed to send message: ${errorCode} - ${errorMessage}`);
// Try fallback without formatting
if (parseMode && errorCode === 400) {
console.log(`[DEBUG] Retrying without parse mode...`);
try {
const fallbackData = { ...requestData };
delete fallbackData.parse_mode;
fallbackData.text = this._createSafeText(messageText);
const fallbackResponse = await axios.post(
`${this.apiBaseUrl}/bot${this.config.botToken}/sendMessage`,
fallbackData,
{
...this._getNetworkOptions(),
timeout: 15000
}
);
const fallbackMessageId = fallbackResponse.data.result.message_id;
console.log(`[DEBUG] ✅ Fallback message sent successfully, ID: ${fallbackMessageId}`);
return { success: true, messageId: fallbackMessageId };
} catch (fallbackError) {
console.log(`[DEBUG] ❌ Fallback also failed: ${fallbackError.response?.data?.description || fallbackError.message}`);
}
}
return { success: false, messageId: null };
}
}
async _createSession(sessionId, notification, token) { async _createSession(sessionId, notification, token) {
const session = { const session = {
id: sessionId, id: sessionId,

View File

@ -40,18 +40,6 @@ class TelegramWebhookHandler {
}); });
} }
/**
* Generate network options for axios requests
* @returns {Object} Network options object
*/
_getNetworkOptions() {
const options = {};
if (this.config.forceIPv4) {
options.family = 4;
}
return options;
}
async _handleWebhook(req, res) { async _handleWebhook(req, res) {
try { try {
const update = req.body; const update = req.body;
@ -238,8 +226,7 @@ class TelegramWebhookHandler {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.apiBaseUrl}/bot${this.config.botToken}/getMe`, `${this.apiBaseUrl}/bot${this.config.botToken}/getMe`
this._getNetworkOptions()
); );
if (response.data.ok && response.data.result.username) { if (response.data.ok && response.data.result.username) {
@ -290,8 +277,7 @@ class TelegramWebhookHandler {
chat_id: chatId, chat_id: chatId,
text: text, text: text,
...options ...options
}, }
this._getNetworkOptions()
); );
} catch (error) { } catch (error) {
this.logger.error('Failed to send message:', error.response?.data || error.message); this.logger.error('Failed to send message:', error.response?.data || error.message);
@ -305,8 +291,7 @@ class TelegramWebhookHandler {
{ {
callback_query_id: callbackQueryId, callback_query_id: callbackQueryId,
text: text text: text
}, }
this._getNetworkOptions()
); );
} catch (error) { } catch (error) {
this.logger.error('Failed to answer callback query:', error.response?.data || error.message); this.logger.error('Failed to answer callback query:', error.response?.data || error.message);
@ -320,8 +305,7 @@ class TelegramWebhookHandler {
{ {
url: webhookUrl, url: webhookUrl,
allowed_updates: ['message', 'callback_query'] allowed_updates: ['message', 'callback_query']
}, }
this._getNetworkOptions()
); );
this.logger.info('Webhook set successfully:', response.data); this.logger.info('Webhook set successfully:', response.data);

View File

@ -96,12 +96,10 @@ class ConfigManager {
}, },
telegram: { telegram: {
type: 'chat', type: 'chat',
enabled: process.env.TELEGRAM_ENABLED === 'true', enabled: false,
config: { config: {
botToken: process.env.TELEGRAM_BOT_TOKEN || '', token: '',
chatId: process.env.TELEGRAM_CHAT_ID || '', chatId: ''
groupId: process.env.TELEGRAM_GROUP_ID || '',
forceIPv4: process.env.TELEGRAM_FORCE_IPV4 === 'true'
} }
} }
}; };

0
src/daemon/taskping-daemon.js Normal file → Executable file
View File

View File

@ -538,464 +538,5 @@
"sessionId": "10cd6e52-91a8-476a-af0a-1fe2c2929ab6", "sessionId": "10cd6e52-91a8-476a-af0a-1fe2c2929ab6",
"tmuxSession": "claude-hook-test", "tmuxSession": "claude-hook-test",
"description": "completed - Claude-Code-Remote" "description": "completed - Claude-Code-Remote"
},
"M4769IDR": {
"type": "pty",
"createdAt": 1756981849,
"expiresAt": 1757068249,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "56e2eb19-9b1e-4b49-b3b3-f99149a9e244",
"tmuxSession": "test-session",
"description": "completed - Claude-Code-Remote-Test"
},
"UG0HP8QJ": {
"type": "pty",
"createdAt": 1756982044,
"expiresAt": 1757068444,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "925e4a47-174f-4b48-a666-12ca707c18d1",
"tmuxSession": "claude-code-remote",
"description": "completed - ecllipse"
},
"OGKAMMS0": {
"type": "pty",
"createdAt": 1756982145,
"expiresAt": 1757068545,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "2cf5a9ef-eb34-4cf0-8118-a2471f557320",
"tmuxSession": "0",
"description": "completed - ecllipse"
},
"IJYBTZ1M": {
"type": "pty",
"createdAt": 1756982185,
"expiresAt": 1757068585,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "6474c5f8-4234-417b-8149-060e6a091035",
"tmuxSession": "0",
"description": "completed - ecllipse"
},
"PQECZXRL": {
"type": "pty",
"createdAt": 1756983489,
"expiresAt": 1757069889,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "1b68620f-7199-40fe-b6fa-e57b3f36e346",
"tmuxSession": "claude-hook-test",
"description": "completed - ecllipse"
},
"BTEAWB0A": {
"type": "pty",
"createdAt": 1756983654,
"expiresAt": 1757070054,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "6a21e7b9-f95b-419f-8267-c7ab939ebb64",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"W2JST4BL": {
"type": "pty",
"createdAt": 1756984158,
"expiresAt": 1757070558,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "70221d98-958e-4084-91ca-971127620375",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"7YGTUNQI": {
"type": "pty",
"createdAt": 1756984735,
"expiresAt": 1757071135,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "b21eecfb-5566-4329-b7f6-e9ee5b8c5b87",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"JN9R6ORI": {
"type": "pty",
"createdAt": 1756984808,
"expiresAt": 1757071208,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "0eb2f38b-b5f3-4cd2-a096-fdbb50f6b4a3",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"G15979CU": {
"type": "pty",
"createdAt": 1756984876,
"expiresAt": 1757071276,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "05abff6e-1f11-4662-bede-9cc40dba095b",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"XO7ZYYCD": {
"type": "pty",
"createdAt": 1756985082,
"expiresAt": 1757071482,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "650de5dc-e5a5-406c-9ff0-e6b658b7639d",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"297WCHFD": {
"type": "pty",
"createdAt": 1756985194,
"expiresAt": 1757071594,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "e3cdc11d-2aef-4a0e-b8cc-0112889498a9",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"TUBTGR8Z": {
"type": "pty",
"createdAt": 1756985273,
"expiresAt": 1757071673,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "e7ca8ded-58ed-4a39-b676-d8fedef37e74",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"5YCUAO3D": {
"type": "pty",
"createdAt": 1756985483,
"expiresAt": 1757071883,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "9c7ab226-b2b3-4a52-b884-37ba050e71ac",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"2VCH8RD0": {
"type": "pty",
"createdAt": 1756986019,
"expiresAt": 1757072419,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "d5eebf40-762c-42f0-9a9e-6f38eb1e131f",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"3WMICU56": {
"type": "pty",
"createdAt": 1756987089,
"expiresAt": 1757073489,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "2a135963-ca49-425f-aa41-433154c4f70d",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"05BC2ZC2": {
"type": "pty",
"createdAt": 1756987248,
"expiresAt": 1757073648,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "84037cac-6d82-454e-ae12-66b59baf9d8f",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"M9SZFQ16": {
"type": "pty",
"createdAt": 1756987334,
"expiresAt": 1757073734,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "562fcfeb-8b0d-4ae2-87c5-6a6fafe7c53f",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"6G3CXZ8Y": {
"type": "pty",
"createdAt": 1756987424,
"expiresAt": 1757073824,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "3cf3c431-0aed-4097-a594-47144bf08a62",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"I5LFQWV0": {
"type": "pty",
"createdAt": 1756987485,
"expiresAt": 1757073885,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "ba716dd3-e2c2-4886-9e42-17c1b8d060ae",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"JLROEO9E": {
"type": "pty",
"createdAt": 1756987634,
"expiresAt": 1757074034,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "3db165f7-e6ea-4d47-82d0-c11f8e32127e",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"K38PPZ6B": {
"type": "pty",
"createdAt": 1756988634,
"expiresAt": 1757075034,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "379f517e-b533-47b8-a9f4-134565e03e6a",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"JFRFCCUN": {
"type": "pty",
"createdAt": 1756990126,
"expiresAt": 1757076526,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "868c8e44-ed03-40bd-bfa1-05c62b852e52",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"TDO4OJHV": {
"type": "pty",
"createdAt": 1756990188,
"expiresAt": 1757076588,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "10dfce8a-7602-4f0d-8c4f-d7292ba769cf",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"L1WV5FAM": {
"type": "pty",
"createdAt": 1756990266,
"expiresAt": 1757076666,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "6f1193e8-da56-4dde-b898-e7e756a86b51",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"U9ERQKL6": {
"type": "pty",
"createdAt": 1756990314,
"expiresAt": 1757076714,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "7e3faaa7-73a4-4cfc-a9b6-34994c7f847f",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"O6D5M0TE": {
"type": "pty",
"createdAt": 1756990348,
"expiresAt": 1757076748,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "e31e9b64-a65b-4336-ad80-d465482842a6",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"A08Q192M": {
"type": "pty",
"createdAt": 1756990442,
"expiresAt": 1757076842,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "51d7c124-9978-4598-ab91-988fd489918f",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"WANYF73Z": {
"type": "pty",
"createdAt": 1756990505,
"expiresAt": 1757076905,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "281cf8ad-6e3b-4add-ba99-81d781b5da94",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"X6ILHKQN": {
"type": "pty",
"createdAt": 1756990584,
"expiresAt": 1757076984,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "0e4ac6fa-8808-4a68-9ebe-b97537c596a9",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"JSJBJIU3": {
"type": "pty",
"createdAt": 1756990621,
"expiresAt": 1757077021,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "dbd1f477-0550-463b-b147-fcdf78720177",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"IS1F9FN9": {
"type": "pty",
"createdAt": 1756991169,
"expiresAt": 1757077569,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "2fa9255d-eb9a-4b37-8852-8204d5730d42",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"CJRIHOH9": {
"type": "pty",
"createdAt": 1756991395,
"expiresAt": 1757077795,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "c5d96613-00de-459c-a8f0-52f020d135fc",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"19318YCF": {
"type": "pty",
"createdAt": 1756991584,
"expiresAt": 1757077984,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "ffba11b1-0aaf-4639-ba4f-6db27249ae22",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"KTP2NLKA": {
"type": "pty",
"createdAt": 1756991689,
"expiresAt": 1757078089,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "c4b49e6a-c4ca-446a-b28c-09863478c9fc",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"3HB36PXA": {
"type": "pty",
"createdAt": 1756991822,
"expiresAt": 1757078222,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "9a14ee0e-cf2d-44f5-bec8-548853fc97e1",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"WLR6CVMN": {
"type": "pty",
"createdAt": 1756991860,
"expiresAt": 1757078260,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "22cd07f1-34c2-4f58-b470-d7e7d235a92b",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"634ZIJ55": {
"type": "pty",
"createdAt": 1756991894,
"expiresAt": 1757078294,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "7016be36-709a-48c5-8f64-2589aae4e5e9",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"HG4Z82VG": {
"type": "pty",
"createdAt": 1756991984,
"expiresAt": 1757078384,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "c42a911c-2b47-4b41-8280-c62889ab2384",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"OIO9DPTN": {
"type": "pty",
"createdAt": 1756992035,
"expiresAt": 1757078435,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "e29d64f2-19f5-4e5b-92c8-6bd1b766bc0d",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"PWB2DEYR": {
"type": "pty",
"createdAt": 1756992167,
"expiresAt": 1757078567,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "197cd53a-4a64-42a9-8262-8e3b429f08b8",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"TJ5VIF92": {
"type": "pty",
"createdAt": 1756992584,
"expiresAt": 1757078984,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "019ab1c3-bde2-4d00-b21e-23167fe52c16",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"J56R0D84": {
"type": "pty",
"createdAt": 1757031172,
"expiresAt": 1757117572,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "cb99ffc3-ee1f-44be-9438-666e9b7fcd54",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"2A26K43N": {
"type": "pty",
"createdAt": 1757031544,
"expiresAt": 1757117944,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "f930e41f-eb86-49d0-85ab-2fcc06aa27e9",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"30W8P8Y1": {
"type": "pty",
"createdAt": 1757031609,
"expiresAt": 1757118009,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "945a07d5-5403-433e-8f90-951e639e0c0c",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"ZNE082NM": {
"type": "pty",
"createdAt": 1757031634,
"expiresAt": 1757118034,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "c30b0f1f-1d4f-438f-a7c8-81fb9d8ddd7c",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"G73CZLO4": {
"type": "pty",
"createdAt": 1757031693,
"expiresAt": 1757118093,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "c4d9c060-83f4-4fc1-b83a-41e3045df66a",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"6GAO026F": {
"type": "pty",
"createdAt": 1757031822,
"expiresAt": 1757118222,
"cwd": "/home/lsamc/develop/ecllipse",
"sessionId": "6feee05d-cdf5-41ab-9eb7-3e2b39be97cd",
"tmuxSession": "claude-session",
"description": "completed - ecllipse"
},
"0ZDCUMDH": {
"type": "pty",
"createdAt": 1757031847,
"expiresAt": 1757118247,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "6b330566-d096-4862-9afe-aa32e038e9e9",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"RJMOLNH2": {
"type": "pty",
"createdAt": 1757032064,
"expiresAt": 1757118464,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "f394b1b0-9ea2-4106-b27a-75842319ed31",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
},
"E1E1RJD2": {
"type": "pty",
"createdAt": 1757032743,
"expiresAt": 1757119143,
"cwd": "/home/lsamc/.local/src/Claude-Code-Remote",
"sessionId": "f0acac1d-9b96-4baa-8622-cb030c300767",
"tmuxSession": "claude-session",
"description": "completed - Claude-Code-Remote"
} }
} }

View File

@ -11,7 +11,7 @@ const Logger = require('../core/logger');
class ControllerInjector { class ControllerInjector {
constructor(config = {}) { constructor(config = {}) {
this.logger = new Logger('ControllerInjector'); this.logger = new Logger('ControllerInjector');
this.mode = config.mode || process.env.INJECTION_MODE || 'tmux'; this.mode = config.mode || process.env.INJECTION_MODE || 'pty';
this.defaultSession = config.defaultSession || process.env.TMUX_SESSION || 'claude-code'; this.defaultSession = config.defaultSession || process.env.TMUX_SESSION || 'claude-code';
} }

View File

@ -609,152 +609,62 @@ class TmuxMonitor extends EventEmitter {
extractConversation(text, sessionName = null) { extractConversation(text, sessionName = null) {
const lines = text.split('\n'); const lines = text.split('\n');
console.log(`[DEBUG] extractConversation called for session: ${sessionName}`);
console.log(`[DEBUG] Total lines in buffer: ${lines.length}`);
console.log(`[DEBUG] Last 5 lines:`, lines.slice(-5));
// Save full buffer for debugging (first 50 and last 50 lines)
if (lines.length > 10) {
console.log(`[DEBUG] Buffer preview - First 10 lines:`, lines.slice(0, 10));
console.log(`[DEBUG] Buffer preview - Last 10 lines:`, lines.slice(-10));
}
let userQuestion = ''; let userQuestion = '';
let claudeResponse = ''; let claudeResponse = '';
let responseLines = []; let responseLines = [];
let lastUserIndex = -1; let inResponse = false;
// First pass: Find the most recent user input // Find the most recent user question and Claude response
for (let i = lines.length - 1; i >= 0; i--) { let inUserInput = false;
let userQuestionLines = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim(); const line = lines[i].trim();
// Detect user input (line starting with "> " followed by content)
if (line.startsWith('> ') && line.length > 2) { if (line.startsWith('> ') && line.length > 2) {
lastUserIndex = i; userQuestionLines = [line.substring(2).trim()];
userQuestion = line.substring(2).trim(); inUserInput = true;
console.log(`[DEBUG] Found user question at line ${i}: ${userQuestion}`); inResponse = false; // Reset response capture
break; responseLines = []; // Clear previous response
}
}
// If we found a user question, look for the response after it // Record user input timestamp if session name provided
if (lastUserIndex >= 0) { if (sessionName) {
let foundResponseStart = false; this.traceCapture.recordUserInput(sessionName);
for (let i = lastUserIndex + 1; i < lines.length; i++) {
const line = lines[i].trim();
// Skip empty lines and system lines, but be more selective
if (!line ||
line.includes('? for shortcuts') ||
line.match(/^[╭╰│─]+$/) ||
line.startsWith('$') ||
line.startsWith('#') ||
line.match(/^\[.*\]$/) || // System messages in brackets
line.includes('(esc to interrupt)') || // Claude Code interrupt message
line.match(/^\+\s*Cascading/) || // Cascading status message
line.match(/^_{3,}$/)) { // Multiple underscores (display artifacts)
console.log(`[DEBUG] Skipping system line: ${line}`);
continue;
} }
// Multiple patterns to detect Claude responses continue;
const isClaudeResponse = }
line.startsWith('⏺ ') || // Traditional marker
line.startsWith('I\'ll ') || // Common Claude opening
line.startsWith('I can ') ||
line.startsWith('Let me ') ||
line.startsWith('Here') ||
line.startsWith('Sure') ||
line.startsWith('Yes') ||
line.startsWith('No') ||
line.includes('```') || // Code blocks
/^[A-Z][a-z]/.test(line); // Sentences starting with capital letter
if (isClaudeResponse && !foundResponseStart) { // Continue capturing multi-line user input
foundResponseStart = true; if (inUserInput && !line.startsWith('⏺') && line.length > 0) {
responseLines = [line.startsWith('⏺ ') ? line.substring(2).trim() : line]; userQuestionLines.push(line);
console.log(`[DEBUG] Found response start at line ${i}: ${line}`); continue;
} else if (foundResponseStart) { }
// Only stop for clear prompt indicators, not for special characters or formatting
if (line.startsWith('> ') && line.length > 2) { // New user input // End of user input
console.log(`[DEBUG] Stopping response collection at new user input: ${line}`); if (inUserInput && (line.startsWith('⏺') || line.length === 0)) {
break; inUserInput = false;
} userQuestion = userQuestionLines.join(' ');
// Stop at command prompt box start (new interaction) }
if (line.startsWith('╭') && line.includes('─')) {
console.log(`[DEBUG] Stopping response collection at prompt box: ${line}`); // Detect Claude response (line starting with "⏺ " or other response indicators)
break; if (line.startsWith('⏺ ') ||
} (inResponse && line.length > 0 &&
// Continue collecting response, ignoring formatting artifacts !line.startsWith('╭') && !line.startsWith('│') && !line.startsWith('╰') &&
!line.startsWith('> ') && !line.includes('? for shortcuts'))) {
if (line.startsWith('⏺ ')) {
inResponse = true;
responseLines = [line.substring(2).trim()]; // Remove "⏺ " prefix
} else if (inResponse) {
responseLines.push(line); responseLines.push(line);
console.log(`[DEBUG] Added response line ${responseLines.length}: ${line.substring(0, 50)}...`);
}
}
}
// Fallback: Look for any Claude response pattern in a larger range
if (responseLines.length === 0) {
console.log(`[DEBUG] No response found after user input, trying fallback method`);
const recentLines = lines.slice(-40); // Increased range
let inFallbackResponse = false;
for (let i = 0; i < recentLines.length; i++) {
const line = recentLines[i].trim();
// Skip problematic lines even in fallback
if (!line ||
line.includes('(esc to interrupt)') ||
line.match(/^\+\s*Cascading/) ||
line.match(/^_{3,}$/) ||
line.includes('? for shortcuts')) {
continue;
}
// Look for Claude response indicators
if (line.startsWith('⏺ ') ||
(line.length > 5 &&
!line.startsWith('> ') &&
!line.startsWith('$') &&
!line.startsWith('#') &&
!line.match(/^\[.*\]$/))) {
const cleanLine = line.startsWith('⏺ ') ? line.substring(2).trim() : line;
responseLines.push(cleanLine);
inFallbackResponse = true;
console.log(`[DEBUG] Fallback found response line: ${cleanLine.substring(0, 50)}...`);
} else if (inFallbackResponse && line.length > 0) {
// Continue collecting if we're in a response and line isn't a clear break
responseLines.push(line);
console.log(`[DEBUG] Fallback continued response: ${line.substring(0, 50)}...`);
}
}
}
// Super fallback: Get any meaningful content if still empty
if (responseLines.length === 0) {
console.log(`[DEBUG] Still no response, trying super fallback - scanning all meaningful lines`);
const meaningfulLines = [];
for (let i = Math.max(0, lastUserIndex + 1); i < lines.length; i++) {
const line = lines[i].trim();
// Collect any line that seems like content (not system/formatting)
if (line.length > 5 &&
!line.includes('? for shortcuts') &&
!line.match(/^[╭╰│─\s]+$/) &&
!line.includes('(esc to interrupt)') &&
!line.match(/^\+\s*Cascading/) &&
!line.match(/^_{3,}$/) &&
!line.startsWith('> ') &&
!line.startsWith('$') &&
!line.startsWith('#')) {
meaningfulLines.push(line);
} }
} }
if (meaningfulLines.length > 0) { // Stop capturing response when we hit another prompt or box boundary
responseLines = meaningfulLines; if (inResponse && (line.startsWith('╭') || line.startsWith('│ > ') || line.includes('? for shortcuts'))) {
console.log(`[DEBUG] Super fallback collected ${meaningfulLines.length} meaningful lines`); inResponse = false;
} }
} }
@ -763,15 +673,27 @@ class TmuxMonitor extends EventEmitter {
// Remove box characters but preserve formatting // Remove box characters but preserve formatting
claudeResponse = claudeResponse claudeResponse = claudeResponse
.replace(/[╭╰]/g, '') .replace(/[╭╰]/g, '')
.replace(/^\s*│\s*/gm, '') .replace(/^\s*│\s*/gm, '')
.replace(/│\s*$/gm, '') // Don't collapse multiple spaces - preserve code formatting
// .replace(/\s+/g, ' ')
.trim(); .trim();
console.log(`[DEBUG] Final extraction result:`); // Don't limit response length - we want the full response
console.log(`[DEBUG] User Question: "${userQuestion}"`); // if (claudeResponse.length > 500) {
console.log(`[DEBUG] Claude Response: "${claudeResponse.substring(0, 100)}${claudeResponse.length > 100 ? '...' : ''}"`); // claudeResponse = claudeResponse.substring(0, 497) + '...';
console.log(`[DEBUG] Response lines count: ${responseLines.length}`); // }
// If we didn't find a question in the standard format, look for any recent text input
if (!userQuestion) {
for (let i = lines.length - 1; i >= 0; i--) {
const line = lines[i].trim();
if (line.startsWith('> ') && line.length > 2) {
userQuestion = line.substring(2).trim();
break;
}
}
}
return { return {
userQuestion: userQuestion || 'No user input', userQuestion: userQuestion || 'No user input',

0
start-all-webhooks.js Normal file → Executable file
View File

0
start-line-webhook.js Normal file → Executable file
View File

0
start-relay-pty.js Normal file → Executable file
View File

0
start-telegram-webhook.js Normal file → Executable file
View File

0
test-complete-flow.sh Normal file → Executable file
View File

2
test-injection.js Normal file → Executable file
View File

@ -31,4 +31,4 @@ async function testInjection() {
} }
} }
testInjection().catch(console.error); testInjection().catch(console.error); < /dev/null

0
test-long-email.js Normal file → Executable file
View File

0
test-telegram-notification.js Normal file → Executable file
View File

0
test-telegram-setup.sh Normal file → Executable file
View File