@jsayubi/ccgram 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +19 -0
- package/LICENSE +21 -0
- package/README.md +338 -0
- package/ccgram.service +24 -0
- package/config/channels.json +58 -0
- package/config/default.json +27 -0
- package/config/defaults/config.json +16 -0
- package/config/defaults/i18n.json +32 -0
- package/config/email-template.json +31 -0
- package/config/test-with-subagent.json +16 -0
- package/config/user.json +27 -0
- package/dist/claude-hook-notify.d.ts +7 -0
- package/dist/claude-hook-notify.d.ts.map +1 -0
- package/dist/claude-hook-notify.js +154 -0
- package/dist/claude-hook-notify.js.map +1 -0
- package/dist/claude-remote.d.ts +50 -0
- package/dist/claude-remote.d.ts.map +1 -0
- package/dist/claude-remote.js +927 -0
- package/dist/claude-remote.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +110 -0
- package/dist/cli.js.map +1 -0
- package/dist/enhanced-hook-notify.d.ts +16 -0
- package/dist/enhanced-hook-notify.d.ts.map +1 -0
- package/dist/enhanced-hook-notify.js +288 -0
- package/dist/enhanced-hook-notify.js.map +1 -0
- package/dist/permission-hook.d.ts +15 -0
- package/dist/permission-hook.d.ts.map +1 -0
- package/dist/permission-hook.js +357 -0
- package/dist/permission-hook.js.map +1 -0
- package/dist/prompt-bridge.d.ts +50 -0
- package/dist/prompt-bridge.d.ts.map +1 -0
- package/dist/prompt-bridge.js +173 -0
- package/dist/prompt-bridge.js.map +1 -0
- package/dist/question-notify.d.ts +16 -0
- package/dist/question-notify.d.ts.map +1 -0
- package/dist/question-notify.js +272 -0
- package/dist/question-notify.js.map +1 -0
- package/dist/setup.d.ts +10 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +649 -0
- package/dist/setup.js.map +1 -0
- package/dist/smart-monitor.d.ts +7 -0
- package/dist/smart-monitor.d.ts.map +1 -0
- package/dist/smart-monitor.js +256 -0
- package/dist/smart-monitor.js.map +1 -0
- package/dist/src/automation/claude-automation.d.ts +45 -0
- package/dist/src/automation/claude-automation.d.ts.map +1 -0
- package/dist/src/automation/claude-automation.js +367 -0
- package/dist/src/automation/claude-automation.js.map +1 -0
- package/dist/src/automation/clipboard-automation.d.ts +35 -0
- package/dist/src/automation/clipboard-automation.d.ts.map +1 -0
- package/dist/src/automation/clipboard-automation.js +242 -0
- package/dist/src/automation/clipboard-automation.js.map +1 -0
- package/dist/src/automation/simple-automation.d.ts +56 -0
- package/dist/src/automation/simple-automation.d.ts.map +1 -0
- package/dist/src/automation/simple-automation.js +283 -0
- package/dist/src/automation/simple-automation.js.map +1 -0
- package/dist/src/channels/base/channel.d.ts +60 -0
- package/dist/src/channels/base/channel.d.ts.map +1 -0
- package/dist/src/channels/base/channel.js +96 -0
- package/dist/src/channels/base/channel.js.map +1 -0
- package/dist/src/channels/email/smtp.d.ts +74 -0
- package/dist/src/channels/email/smtp.d.ts.map +1 -0
- package/dist/src/channels/email/smtp.js +605 -0
- package/dist/src/channels/email/smtp.js.map +1 -0
- package/dist/src/channels/line/line.d.ts +36 -0
- package/dist/src/channels/line/line.d.ts.map +1 -0
- package/dist/src/channels/line/line.js +180 -0
- package/dist/src/channels/line/line.js.map +1 -0
- package/dist/src/channels/line/webhook.d.ts +55 -0
- package/dist/src/channels/line/webhook.d.ts.map +1 -0
- package/dist/src/channels/line/webhook.js +191 -0
- package/dist/src/channels/line/webhook.js.map +1 -0
- package/dist/src/channels/local/desktop.d.ts +30 -0
- package/dist/src/channels/local/desktop.d.ts.map +1 -0
- package/dist/src/channels/local/desktop.js +161 -0
- package/dist/src/channels/local/desktop.js.map +1 -0
- package/dist/src/channels/telegram/telegram.d.ts +43 -0
- package/dist/src/channels/telegram/telegram.d.ts.map +1 -0
- package/dist/src/channels/telegram/telegram.js +223 -0
- package/dist/src/channels/telegram/telegram.js.map +1 -0
- package/dist/src/channels/telegram/webhook.d.ts +75 -0
- package/dist/src/channels/telegram/webhook.d.ts.map +1 -0
- package/dist/src/channels/telegram/webhook.js +278 -0
- package/dist/src/channels/telegram/webhook.js.map +1 -0
- package/dist/src/config-manager.d.ts +16 -0
- package/dist/src/config-manager.d.ts.map +1 -0
- package/dist/src/config-manager.js +152 -0
- package/dist/src/config-manager.js.map +1 -0
- package/dist/src/core/config.d.ts +28 -0
- package/dist/src/core/config.d.ts.map +1 -0
- package/dist/src/core/config.js +248 -0
- package/dist/src/core/config.js.map +1 -0
- package/dist/src/core/logger.d.ts +19 -0
- package/dist/src/core/logger.d.ts.map +1 -0
- package/dist/src/core/logger.js +47 -0
- package/dist/src/core/logger.js.map +1 -0
- package/dist/src/core/notifier.d.ts +45 -0
- package/dist/src/core/notifier.d.ts.map +1 -0
- package/dist/src/core/notifier.js +189 -0
- package/dist/src/core/notifier.js.map +1 -0
- package/dist/src/daemon/taskping-daemon.d.ts +38 -0
- package/dist/src/daemon/taskping-daemon.d.ts.map +1 -0
- package/dist/src/daemon/taskping-daemon.js +306 -0
- package/dist/src/daemon/taskping-daemon.js.map +1 -0
- package/dist/src/relay/claude-command-bridge.d.ts +57 -0
- package/dist/src/relay/claude-command-bridge.d.ts.map +1 -0
- package/dist/src/relay/claude-command-bridge.js +188 -0
- package/dist/src/relay/claude-command-bridge.js.map +1 -0
- package/dist/src/relay/command-relay.d.ts +94 -0
- package/dist/src/relay/command-relay.d.ts.map +1 -0
- package/dist/src/relay/command-relay.js +463 -0
- package/dist/src/relay/command-relay.js.map +1 -0
- package/dist/src/relay/email-listener.d.ts +65 -0
- package/dist/src/relay/email-listener.d.ts.map +1 -0
- package/dist/src/relay/email-listener.js +460 -0
- package/dist/src/relay/email-listener.js.map +1 -0
- package/dist/src/relay/relay-pty.d.ts +21 -0
- package/dist/src/relay/relay-pty.d.ts.map +1 -0
- package/dist/src/relay/relay-pty.js +696 -0
- package/dist/src/relay/relay-pty.js.map +1 -0
- package/dist/src/relay/smart-injector.d.ts +30 -0
- package/dist/src/relay/smart-injector.d.ts.map +1 -0
- package/dist/src/relay/smart-injector.js +233 -0
- package/dist/src/relay/smart-injector.js.map +1 -0
- package/dist/src/relay/tmux-injector.d.ts +46 -0
- package/dist/src/relay/tmux-injector.d.ts.map +1 -0
- package/dist/src/relay/tmux-injector.js +413 -0
- package/dist/src/relay/tmux-injector.js.map +1 -0
- package/dist/src/tools/config-manager.d.ts +33 -0
- package/dist/src/tools/config-manager.d.ts.map +1 -0
- package/dist/src/tools/config-manager.js +448 -0
- package/dist/src/tools/config-manager.js.map +1 -0
- package/dist/src/tools/installer.d.ts +38 -0
- package/dist/src/tools/installer.d.ts.map +1 -0
- package/dist/src/tools/installer.js +222 -0
- package/dist/src/tools/installer.js.map +1 -0
- package/dist/src/types/callbacks.d.ts +29 -0
- package/dist/src/types/callbacks.d.ts.map +1 -0
- package/dist/src/types/callbacks.js +7 -0
- package/dist/src/types/callbacks.js.map +1 -0
- package/dist/src/types/config.d.ts +56 -0
- package/dist/src/types/config.d.ts.map +1 -0
- package/dist/src/types/config.js +6 -0
- package/dist/src/types/config.js.map +1 -0
- package/dist/src/types/hooks.d.ts +47 -0
- package/dist/src/types/hooks.d.ts.map +1 -0
- package/dist/src/types/hooks.js +6 -0
- package/dist/src/types/hooks.js.map +1 -0
- package/dist/src/types/index.d.ts +7 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +23 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/types/ipc.d.ts +43 -0
- package/dist/src/types/ipc.d.ts.map +1 -0
- package/dist/src/types/ipc.js +7 -0
- package/dist/src/types/ipc.js.map +1 -0
- package/dist/src/types/session.d.ts +70 -0
- package/dist/src/types/session.d.ts.map +1 -0
- package/dist/src/types/session.js +9 -0
- package/dist/src/types/session.js.map +1 -0
- package/dist/src/types/telegram.d.ts +58 -0
- package/dist/src/types/telegram.d.ts.map +1 -0
- package/dist/src/types/telegram.js +6 -0
- package/dist/src/types/telegram.js.map +1 -0
- package/dist/src/utils/active-check.d.ts +19 -0
- package/dist/src/utils/active-check.d.ts.map +1 -0
- package/dist/src/utils/active-check.js +41 -0
- package/dist/src/utils/active-check.js.map +1 -0
- package/dist/src/utils/callback-parser.d.ts +21 -0
- package/dist/src/utils/callback-parser.d.ts.map +1 -0
- package/dist/src/utils/callback-parser.js +58 -0
- package/dist/src/utils/callback-parser.js.map +1 -0
- package/dist/src/utils/controller-injector.d.ts +21 -0
- package/dist/src/utils/controller-injector.d.ts.map +1 -0
- package/dist/src/utils/controller-injector.js +108 -0
- package/dist/src/utils/controller-injector.js.map +1 -0
- package/dist/src/utils/conversation-tracker.d.ts +32 -0
- package/dist/src/utils/conversation-tracker.d.ts.map +1 -0
- package/dist/src/utils/conversation-tracker.js +119 -0
- package/dist/src/utils/conversation-tracker.js.map +1 -0
- package/dist/src/utils/http-request.d.ts +25 -0
- package/dist/src/utils/http-request.d.ts.map +1 -0
- package/dist/src/utils/http-request.js +66 -0
- package/dist/src/utils/http-request.js.map +1 -0
- package/dist/src/utils/optional-require.d.ts +13 -0
- package/dist/src/utils/optional-require.d.ts.map +1 -0
- package/dist/src/utils/optional-require.js +37 -0
- package/dist/src/utils/optional-require.js.map +1 -0
- package/dist/src/utils/paths.d.ts +11 -0
- package/dist/src/utils/paths.d.ts.map +1 -0
- package/dist/src/utils/paths.js +28 -0
- package/dist/src/utils/paths.js.map +1 -0
- package/dist/src/utils/pty-session-manager.d.ts +42 -0
- package/dist/src/utils/pty-session-manager.d.ts.map +1 -0
- package/dist/src/utils/pty-session-manager.js +182 -0
- package/dist/src/utils/pty-session-manager.js.map +1 -0
- package/dist/src/utils/subagent-tracker.d.ts +64 -0
- package/dist/src/utils/subagent-tracker.d.ts.map +1 -0
- package/dist/src/utils/subagent-tracker.js +191 -0
- package/dist/src/utils/subagent-tracker.js.map +1 -0
- package/dist/src/utils/tmux-monitor.d.ts +102 -0
- package/dist/src/utils/tmux-monitor.d.ts.map +1 -0
- package/dist/src/utils/tmux-monitor.js +642 -0
- package/dist/src/utils/tmux-monitor.js.map +1 -0
- package/dist/src/utils/trace-capture.d.ts +42 -0
- package/dist/src/utils/trace-capture.d.ts.map +1 -0
- package/dist/src/utils/trace-capture.js +102 -0
- package/dist/src/utils/trace-capture.js.map +1 -0
- package/dist/start-all-webhooks.d.ts +7 -0
- package/dist/start-all-webhooks.d.ts.map +1 -0
- package/dist/start-all-webhooks.js +98 -0
- package/dist/start-all-webhooks.js.map +1 -0
- package/dist/start-line-webhook.d.ts +7 -0
- package/dist/start-line-webhook.d.ts.map +1 -0
- package/dist/start-line-webhook.js +59 -0
- package/dist/start-line-webhook.js.map +1 -0
- package/dist/start-relay-pty.d.ts +7 -0
- package/dist/start-relay-pty.d.ts.map +1 -0
- package/dist/start-relay-pty.js +173 -0
- package/dist/start-relay-pty.js.map +1 -0
- package/dist/start-telegram-webhook.d.ts +7 -0
- package/dist/start-telegram-webhook.d.ts.map +1 -0
- package/dist/start-telegram-webhook.js +80 -0
- package/dist/start-telegram-webhook.js.map +1 -0
- package/dist/user-prompt-hook.d.ts +13 -0
- package/dist/user-prompt-hook.d.ts.map +1 -0
- package/dist/user-prompt-hook.js +45 -0
- package/dist/user-prompt-hook.js.map +1 -0
- package/dist/workspace-router.d.ts +78 -0
- package/dist/workspace-router.d.ts.map +1 -0
- package/dist/workspace-router.js +408 -0
- package/dist/workspace-router.js.map +1 -0
- package/dist/workspace-telegram-bot.d.ts +3 -0
- package/dist/workspace-telegram-bot.d.ts.map +1 -0
- package/dist/workspace-telegram-bot.js +1172 -0
- package/dist/workspace-telegram-bot.js.map +1 -0
- package/package.json +80 -0
- package/src/types/callbacks.ts +39 -0
- package/src/types/config.ts +63 -0
- package/src/types/hooks.ts +50 -0
- package/src/types/index.ts +6 -0
- package/src/types/ipc.ts +55 -0
- package/src/types/session.ts +72 -0
- package/src/types/telegram.ts +66 -0
|
@@ -0,0 +1,927 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* CCGram - Claude Code Smart Notification System
|
|
5
|
+
* Main entry point for the CLI tool
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
// Load environment variables from CCGram directory
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const paths_1 = require("./src/utils/paths");
|
|
13
|
+
const envPath = path_1.default.join(paths_1.PROJECT_ROOT, '.env');
|
|
14
|
+
require('dotenv').config({ path: envPath });
|
|
15
|
+
const logger_1 = __importDefault(require("./src/core/logger"));
|
|
16
|
+
const notifier_1 = __importDefault(require("./src/core/notifier"));
|
|
17
|
+
const config_1 = __importDefault(require("./src/core/config"));
|
|
18
|
+
class ClaudeCodeRemoteCLI {
|
|
19
|
+
logger;
|
|
20
|
+
config;
|
|
21
|
+
notifier;
|
|
22
|
+
constructor() {
|
|
23
|
+
this.logger = new logger_1.default('CLI');
|
|
24
|
+
this.config = new config_1.default();
|
|
25
|
+
this.notifier = new notifier_1.default(this.config);
|
|
26
|
+
}
|
|
27
|
+
async init() {
|
|
28
|
+
// Load configuration
|
|
29
|
+
this.config.load();
|
|
30
|
+
// Initialize channels
|
|
31
|
+
await this.notifier.initializeChannels();
|
|
32
|
+
}
|
|
33
|
+
async run() {
|
|
34
|
+
const args = process.argv.slice(2);
|
|
35
|
+
const command = args[0];
|
|
36
|
+
try {
|
|
37
|
+
await this.init();
|
|
38
|
+
switch (command) {
|
|
39
|
+
case 'notify':
|
|
40
|
+
await this.handleNotify(args.slice(1));
|
|
41
|
+
break;
|
|
42
|
+
case 'test':
|
|
43
|
+
await this.handleTest(args.slice(1));
|
|
44
|
+
break;
|
|
45
|
+
case 'status':
|
|
46
|
+
await this.handleStatus(args.slice(1));
|
|
47
|
+
break;
|
|
48
|
+
case 'config':
|
|
49
|
+
await this.handleConfig(args.slice(1));
|
|
50
|
+
break;
|
|
51
|
+
case 'install':
|
|
52
|
+
await this.handleInstall(args.slice(1));
|
|
53
|
+
break;
|
|
54
|
+
case 'relay':
|
|
55
|
+
await this.handleRelay(args.slice(1));
|
|
56
|
+
break;
|
|
57
|
+
case 'edit-config':
|
|
58
|
+
await this.handleEditConfig(args.slice(1));
|
|
59
|
+
break;
|
|
60
|
+
case 'setup-email':
|
|
61
|
+
await this.handleSetupEmail(args.slice(1));
|
|
62
|
+
break;
|
|
63
|
+
case 'daemon':
|
|
64
|
+
await this.handleDaemon(args.slice(1));
|
|
65
|
+
break;
|
|
66
|
+
case 'commands':
|
|
67
|
+
await this.handleCommands(args.slice(1));
|
|
68
|
+
break;
|
|
69
|
+
case 'test-paste':
|
|
70
|
+
await this.handleTestPaste(args.slice(1));
|
|
71
|
+
break;
|
|
72
|
+
case 'test-simple':
|
|
73
|
+
await this.handleTestSimple(args.slice(1));
|
|
74
|
+
break;
|
|
75
|
+
case 'test-claude':
|
|
76
|
+
await this.handleTestClaude(args.slice(1));
|
|
77
|
+
break;
|
|
78
|
+
case 'setup-permissions':
|
|
79
|
+
await this.handleSetupPermissions(args.slice(1));
|
|
80
|
+
break;
|
|
81
|
+
case 'diagnose':
|
|
82
|
+
await this.handleDiagnose(args.slice(1));
|
|
83
|
+
break;
|
|
84
|
+
case '--help':
|
|
85
|
+
case '-h':
|
|
86
|
+
case undefined:
|
|
87
|
+
this.showHelp();
|
|
88
|
+
break;
|
|
89
|
+
default:
|
|
90
|
+
console.error(`Unknown command: ${command}`);
|
|
91
|
+
this.showHelp();
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
this.logger.error('CLI error:', error.message);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async handleNotify(args) {
|
|
101
|
+
const typeIndex = args.findIndex(arg => arg === '--type');
|
|
102
|
+
if (typeIndex === -1 || typeIndex + 1 >= args.length) {
|
|
103
|
+
console.error('Usage: claude-remote notify --type <completed|waiting>');
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
const type = args[typeIndex + 1];
|
|
107
|
+
if (!['completed', 'waiting'].includes(type)) {
|
|
108
|
+
console.error('Invalid type. Use: completed or waiting');
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
// Automatically capture current tmux session conversation content
|
|
112
|
+
const metadata = await this.captureCurrentConversation();
|
|
113
|
+
// Handle subagent notifications
|
|
114
|
+
if (type === 'waiting') {
|
|
115
|
+
const Config = require('./src/core/config');
|
|
116
|
+
const config = new Config();
|
|
117
|
+
config.load();
|
|
118
|
+
const enableSubagentNotifications = config.get('enableSubagentNotifications', false);
|
|
119
|
+
if (!enableSubagentNotifications) {
|
|
120
|
+
// Instead of skipping, track the subagent activity
|
|
121
|
+
const SubagentTracker = require('./src/utils/subagent-tracker');
|
|
122
|
+
const tracker = new SubagentTracker();
|
|
123
|
+
// Use tmux session as the tracking key
|
|
124
|
+
const trackingKey = metadata.tmuxSession || 'default';
|
|
125
|
+
// Capture more detailed information about the subagent activity
|
|
126
|
+
const activityDetails = {
|
|
127
|
+
userQuestion: metadata.userQuestion || 'No question captured',
|
|
128
|
+
claudeResponse: metadata.claudeResponse || 'No response captured',
|
|
129
|
+
timestamp: new Date().toISOString(),
|
|
130
|
+
tmuxSession: metadata.tmuxSession
|
|
131
|
+
};
|
|
132
|
+
// Don't truncate the response too aggressively
|
|
133
|
+
if (activityDetails.claudeResponse && activityDetails.claudeResponse.length > 1000) {
|
|
134
|
+
activityDetails.claudeResponse = activityDetails.claudeResponse.substring(0, 1000) + '...[see full output in tmux]';
|
|
135
|
+
}
|
|
136
|
+
tracker.addActivity(trackingKey, {
|
|
137
|
+
type: 'SubagentStop',
|
|
138
|
+
description: metadata.userQuestion || 'Subagent task',
|
|
139
|
+
details: activityDetails
|
|
140
|
+
});
|
|
141
|
+
this.logger.info(`Subagent activity tracked for tmux session: ${trackingKey}`);
|
|
142
|
+
process.exit(0);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// For completed notifications, include subagent activities and execution trace
|
|
146
|
+
if (type === 'completed') {
|
|
147
|
+
const Config = require('./src/core/config');
|
|
148
|
+
const config = new Config();
|
|
149
|
+
config.load();
|
|
150
|
+
const showSubagentActivitiesInEmail = config.get('showSubagentActivitiesInEmail', false);
|
|
151
|
+
if (showSubagentActivitiesInEmail) {
|
|
152
|
+
const SubagentTracker = require('./src/utils/subagent-tracker');
|
|
153
|
+
const tracker = new SubagentTracker();
|
|
154
|
+
const trackingKey = metadata.tmuxSession || 'default';
|
|
155
|
+
// Get and format subagent activities
|
|
156
|
+
const subagentSummary = tracker.formatActivitiesForEmail(trackingKey);
|
|
157
|
+
if (subagentSummary) {
|
|
158
|
+
metadata.subagentActivities = subagentSummary;
|
|
159
|
+
}
|
|
160
|
+
// Clear activities after including them in the notification
|
|
161
|
+
tracker.clearActivities(trackingKey);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
// Always clear activities even if not showing them
|
|
165
|
+
const SubagentTracker = require('./src/utils/subagent-tracker');
|
|
166
|
+
const tracker = new SubagentTracker();
|
|
167
|
+
const trackingKey = metadata.tmuxSession || 'default';
|
|
168
|
+
tracker.clearActivities(trackingKey);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const result = await this.notifier.notify(type, metadata);
|
|
172
|
+
if (result.success) {
|
|
173
|
+
this.logger.info(`${type} notification sent successfully`);
|
|
174
|
+
process.exit(0);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
this.logger.error(`Failed to send ${type} notification`);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async captureCurrentConversation() {
|
|
182
|
+
try {
|
|
183
|
+
const { execSync } = require('child_process');
|
|
184
|
+
const TmuxMonitor = require('./src/utils/tmux-monitor');
|
|
185
|
+
// Get current tmux session name
|
|
186
|
+
let currentSession = null;
|
|
187
|
+
try {
|
|
188
|
+
currentSession = execSync('tmux display-message -p "#S"', {
|
|
189
|
+
encoding: 'utf8',
|
|
190
|
+
stdio: ['ignore', 'pipe', 'ignore']
|
|
191
|
+
}).trim();
|
|
192
|
+
}
|
|
193
|
+
catch (e) {
|
|
194
|
+
// Not running in tmux, return empty metadata
|
|
195
|
+
return {};
|
|
196
|
+
}
|
|
197
|
+
if (!currentSession) {
|
|
198
|
+
return {};
|
|
199
|
+
}
|
|
200
|
+
// Use TmuxMonitor to capture conversation
|
|
201
|
+
const tmuxMonitor = new TmuxMonitor();
|
|
202
|
+
const conversation = tmuxMonitor.getRecentConversation(currentSession);
|
|
203
|
+
const fullTrace = tmuxMonitor.getFullExecutionTrace(currentSession);
|
|
204
|
+
return {
|
|
205
|
+
userQuestion: conversation.userQuestion,
|
|
206
|
+
claudeResponse: conversation.claudeResponse,
|
|
207
|
+
tmuxSession: currentSession,
|
|
208
|
+
fullExecutionTrace: fullTrace
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
this.logger.debug('Failed to capture conversation:', error.message);
|
|
213
|
+
return {};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async handleTest(args) {
|
|
217
|
+
console.log('Testing notification channels...\n');
|
|
218
|
+
const results = await this.notifier.test();
|
|
219
|
+
for (const [channel, result] of Object.entries(results)) {
|
|
220
|
+
const status = result.success ? 'ā
PASS' : 'ā FAIL';
|
|
221
|
+
console.log(`${channel}: ${status}`);
|
|
222
|
+
if (result.error) {
|
|
223
|
+
console.log(` Error: ${result.error}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const passCount = Object.values(results).filter(r => r.success).length;
|
|
227
|
+
const totalCount = Object.keys(results).length;
|
|
228
|
+
console.log(`\nTest completed: ${passCount}/${totalCount} channels passed`);
|
|
229
|
+
if (passCount === 0) {
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async handleStatus(args) {
|
|
234
|
+
const status = this.notifier.getStatus();
|
|
235
|
+
console.log('CCGram Status\n');
|
|
236
|
+
console.log('Configuration:');
|
|
237
|
+
console.log(` Enabled: ${status.enabled ? 'Yes' : 'No'}`);
|
|
238
|
+
console.log(` Language: ${status.config.language}`);
|
|
239
|
+
console.log(` Sounds: ${status.config.sound.completed} / ${status.config.sound.waiting}`);
|
|
240
|
+
console.log('\nChannels:');
|
|
241
|
+
// Display all available channels, including disabled ones
|
|
242
|
+
const allChannels = (this.config._channels || {});
|
|
243
|
+
const activeChannels = (status.channels || {});
|
|
244
|
+
// Merge all channel information
|
|
245
|
+
const channelNames = new Set([
|
|
246
|
+
...Object.keys(allChannels),
|
|
247
|
+
...Object.keys(activeChannels)
|
|
248
|
+
]);
|
|
249
|
+
for (const name of channelNames) {
|
|
250
|
+
const channelConfig = allChannels[name] || {};
|
|
251
|
+
const channelStatus = activeChannels[name];
|
|
252
|
+
let enabled, configured, relay;
|
|
253
|
+
if (channelStatus) {
|
|
254
|
+
// Active channel, use actual status
|
|
255
|
+
enabled = channelStatus.enabled ? 'ā
' : 'ā';
|
|
256
|
+
configured = channelStatus.configured ? 'ā
' : 'ā';
|
|
257
|
+
relay = channelStatus.supportsRelay ? 'ā
' : 'ā';
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
// Inactive channel, use configuration status
|
|
261
|
+
enabled = channelConfig.enabled ? 'ā
' : 'ā';
|
|
262
|
+
configured = this._isChannelConfigured(name, channelConfig) ? 'ā
' : 'ā';
|
|
263
|
+
relay = this._supportsRelay(name) ? 'ā
' : 'ā';
|
|
264
|
+
}
|
|
265
|
+
console.log(` ${name}:`);
|
|
266
|
+
console.log(` Enabled: ${enabled}`);
|
|
267
|
+
console.log(` Configured: ${configured}`);
|
|
268
|
+
console.log(` Supports Relay: ${relay}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
_isChannelConfigured(name, config) {
|
|
272
|
+
switch (name) {
|
|
273
|
+
case 'desktop':
|
|
274
|
+
return true; // Desktop notifications don't need special configuration
|
|
275
|
+
case 'email':
|
|
276
|
+
return config.config &&
|
|
277
|
+
config.config.smtp &&
|
|
278
|
+
config.config.smtp.host &&
|
|
279
|
+
config.config.smtp.auth &&
|
|
280
|
+
config.config.smtp.auth.user &&
|
|
281
|
+
config.config.to;
|
|
282
|
+
default:
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
_supportsRelay(name) {
|
|
287
|
+
switch (name) {
|
|
288
|
+
case 'email':
|
|
289
|
+
return true;
|
|
290
|
+
case 'desktop':
|
|
291
|
+
default:
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
async handleConfig(args) {
|
|
296
|
+
// Launch the configuration tool
|
|
297
|
+
const ConfigTool = require('./src/tools/config-manager');
|
|
298
|
+
const configTool = new ConfigTool(this.config);
|
|
299
|
+
await configTool.run(args);
|
|
300
|
+
}
|
|
301
|
+
async handleInstall(args) {
|
|
302
|
+
// Launch the installer
|
|
303
|
+
const Installer = require('./src/tools/installer');
|
|
304
|
+
const installer = new Installer(this.config);
|
|
305
|
+
await installer.run(args);
|
|
306
|
+
}
|
|
307
|
+
async handleRelay(args) {
|
|
308
|
+
const subcommand = args[0];
|
|
309
|
+
switch (subcommand) {
|
|
310
|
+
case 'start':
|
|
311
|
+
await this.startRelay(args.slice(1));
|
|
312
|
+
break;
|
|
313
|
+
case 'stop':
|
|
314
|
+
await this.stopRelay(args.slice(1));
|
|
315
|
+
break;
|
|
316
|
+
case 'status':
|
|
317
|
+
await this.relayStatus(args.slice(1));
|
|
318
|
+
break;
|
|
319
|
+
case 'cleanup':
|
|
320
|
+
await this.cleanupRelay(args.slice(1));
|
|
321
|
+
break;
|
|
322
|
+
default:
|
|
323
|
+
console.error('Usage: claude-remote relay <start|stop|status|cleanup>');
|
|
324
|
+
console.log('');
|
|
325
|
+
console.log('Commands:');
|
|
326
|
+
console.log(' start Start email command relay service');
|
|
327
|
+
console.log(' stop Stop email command relay service');
|
|
328
|
+
console.log(' status View relay service status');
|
|
329
|
+
console.log(' cleanup Clean up completed command history');
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async startRelay(args) {
|
|
334
|
+
try {
|
|
335
|
+
const CommandRelayService = require('./src/relay/command-relay');
|
|
336
|
+
const emailConfig = this.config.getChannel('email');
|
|
337
|
+
if (!emailConfig || !emailConfig.enabled) {
|
|
338
|
+
console.error('ā Email channel not configured or disabled');
|
|
339
|
+
console.log('Please run first: claude-remote config');
|
|
340
|
+
process.exit(1);
|
|
341
|
+
}
|
|
342
|
+
console.log('š Starting email command relay service...');
|
|
343
|
+
const relayService = new CommandRelayService(emailConfig.config);
|
|
344
|
+
// Listen for events
|
|
345
|
+
relayService.on('started', () => {
|
|
346
|
+
console.log('ā
Command relay service started');
|
|
347
|
+
console.log('š§ Listening for email replies...');
|
|
348
|
+
console.log('š” You can now remotely execute Claude Code commands by replying to emails');
|
|
349
|
+
console.log('');
|
|
350
|
+
console.log('Press Ctrl+C to stop the service');
|
|
351
|
+
});
|
|
352
|
+
relayService.on('commandQueued', (command) => {
|
|
353
|
+
console.log(`šØ Received new command: ${command.command.substring(0, 50)}...`);
|
|
354
|
+
});
|
|
355
|
+
relayService.on('commandExecuted', (command) => {
|
|
356
|
+
console.log(`ā
Command executed successfully: ${command.id}`);
|
|
357
|
+
});
|
|
358
|
+
relayService.on('commandFailed', (command, error) => {
|
|
359
|
+
console.log(`ā Command execution failed: ${command.id} - ${error.message}`);
|
|
360
|
+
});
|
|
361
|
+
// Handle graceful shutdown
|
|
362
|
+
process.on('SIGINT', async () => {
|
|
363
|
+
console.log('\nš Stopping command relay service...');
|
|
364
|
+
await relayService.stop();
|
|
365
|
+
console.log('ā
Service stopped');
|
|
366
|
+
process.exit(0);
|
|
367
|
+
});
|
|
368
|
+
// Start service
|
|
369
|
+
await relayService.start();
|
|
370
|
+
// Keep process running
|
|
371
|
+
process.stdin.resume();
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
console.error('ā Failed to start relay service:', error.message);
|
|
375
|
+
process.exit(1);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
async stopRelay(args) {
|
|
379
|
+
console.log('š” Command relay service usually stopped with Ctrl+C');
|
|
380
|
+
console.log('If the service is still running, please find the corresponding process and terminate it manually');
|
|
381
|
+
}
|
|
382
|
+
async relayStatus(args) {
|
|
383
|
+
try {
|
|
384
|
+
const fs = require('fs');
|
|
385
|
+
const stateFile = path_1.default.join(paths_1.PROJECT_ROOT, 'src/data/relay-state.json');
|
|
386
|
+
console.log('š Command relay service status\n');
|
|
387
|
+
// Check email configuration
|
|
388
|
+
const emailConfig = this.config.getChannel('email');
|
|
389
|
+
if (!emailConfig || !emailConfig.enabled) {
|
|
390
|
+
console.log('ā Email channel not configured');
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
const emailCfg = emailConfig.config;
|
|
394
|
+
console.log('ā
Email configuration enabled');
|
|
395
|
+
console.log(`š§ SMTP: ${emailCfg.smtp.host}:${emailCfg.smtp.port}`);
|
|
396
|
+
console.log(`š„ IMAP: ${emailCfg.imap.host}:${emailCfg.imap.port}`);
|
|
397
|
+
console.log(`š¬ Recipient: ${emailCfg.to}`);
|
|
398
|
+
// Check relay status
|
|
399
|
+
if (fs.existsSync(stateFile)) {
|
|
400
|
+
const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
|
|
401
|
+
console.log(`\nš Command queue: ${state.commandQueue?.length || 0} commands`);
|
|
402
|
+
if (state.commandQueue && state.commandQueue.length > 0) {
|
|
403
|
+
console.log('\nRecent commands:');
|
|
404
|
+
state.commandQueue.slice(-5).forEach((cmd) => {
|
|
405
|
+
const cmdStatus = cmd.status === 'completed' ? 'ā
' :
|
|
406
|
+
cmd.status === 'failed' ? 'ā' :
|
|
407
|
+
cmd.status === 'executing' ? 'ā³' : 'āøļø';
|
|
408
|
+
console.log(` ${cmdStatus} ${cmd.id}: ${cmd.command.substring(0, 50)}...`);
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
console.log('\nš No command history found');
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
catch (error) {
|
|
417
|
+
console.error('ā Failed to get status:', error.message);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
async cleanupRelay(args) {
|
|
421
|
+
try {
|
|
422
|
+
const fs = require('fs');
|
|
423
|
+
const stateFile = path_1.default.join(paths_1.PROJECT_ROOT, 'src/data/relay-state.json');
|
|
424
|
+
if (!fs.existsSync(stateFile)) {
|
|
425
|
+
console.log('š No cleanup needed, no command history found');
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
|
|
429
|
+
const beforeCount = state.commandQueue?.length || 0;
|
|
430
|
+
// Clean up completed commands (keep those within 24 hours)
|
|
431
|
+
const cutoff = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
|
432
|
+
state.commandQueue = (state.commandQueue || []).filter((cmd) => cmd.status !== 'completed' ||
|
|
433
|
+
new Date(cmd.completedAt || cmd.queuedAt) > cutoff);
|
|
434
|
+
const afterCount = state.commandQueue.length;
|
|
435
|
+
const removedCount = beforeCount - afterCount;
|
|
436
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
|
|
437
|
+
console.log(`š§¹ Cleanup completed: removed ${removedCount} completed commands`);
|
|
438
|
+
console.log(`š ${afterCount} commands remaining in queue`);
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
console.error('ā Cleanup failed:', error.message);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
async handleEditConfig(args) {
|
|
445
|
+
const { spawn } = require('child_process');
|
|
446
|
+
const configType = args[0];
|
|
447
|
+
if (!configType) {
|
|
448
|
+
console.log('Available configuration files:');
|
|
449
|
+
console.log(' user - User personal configuration (config/user.json)');
|
|
450
|
+
console.log(' channels - Notification channel configuration (config/channels.json)');
|
|
451
|
+
console.log(' default - Default configuration template (config/default.json)');
|
|
452
|
+
console.log('');
|
|
453
|
+
console.log('Usage: claude-remote edit-config <configuration-type>');
|
|
454
|
+
console.log('Example: claude-remote edit-config channels');
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
const configFiles = {
|
|
458
|
+
'user': path_1.default.join(paths_1.PROJECT_ROOT, 'config/user.json'),
|
|
459
|
+
'channels': path_1.default.join(paths_1.PROJECT_ROOT, 'config/channels.json'),
|
|
460
|
+
'default': path_1.default.join(paths_1.PROJECT_ROOT, 'config/default.json')
|
|
461
|
+
};
|
|
462
|
+
const configFile = configFiles[configType];
|
|
463
|
+
if (!configFile) {
|
|
464
|
+
console.error('ā Invalid configuration type:', configType);
|
|
465
|
+
console.log('Available types: user, channels, default');
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
// Check if file exists
|
|
469
|
+
const fs = require('fs');
|
|
470
|
+
if (!fs.existsSync(configFile)) {
|
|
471
|
+
console.error('ā Configuration file does not exist:', configFile);
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
console.log(`š Opening configuration file: ${configFile}`);
|
|
475
|
+
console.log('š” Save and close the editor after editing to take effect');
|
|
476
|
+
console.log('');
|
|
477
|
+
// Determine the editor to use
|
|
478
|
+
const editor = process.env.EDITOR || process.env.VISUAL || this._getDefaultEditor();
|
|
479
|
+
try {
|
|
480
|
+
const editorProcess = spawn(editor, [configFile], {
|
|
481
|
+
stdio: 'inherit'
|
|
482
|
+
});
|
|
483
|
+
editorProcess.on('close', (code) => {
|
|
484
|
+
if (code === 0) {
|
|
485
|
+
console.log('ā
Configuration file saved');
|
|
486
|
+
console.log('š” Run "claude-remote status" to view updated configuration');
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
console.log('ā Editor exited abnormally');
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
editorProcess.on('error', (error) => {
|
|
493
|
+
console.error('ā Unable to start editor:', error.message);
|
|
494
|
+
console.log('');
|
|
495
|
+
console.log('š” You can manually edit the configuration file:');
|
|
496
|
+
console.log(` ${configFile}`);
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
catch (error) {
|
|
500
|
+
console.error('ā Failed to start editor:', error.message);
|
|
501
|
+
console.log('');
|
|
502
|
+
console.log('š” You can manually edit the configuration file:');
|
|
503
|
+
console.log(` ${configFile}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
_getDefaultEditor() {
|
|
507
|
+
// Determine default editor based on platform
|
|
508
|
+
if (process.platform === 'win32') {
|
|
509
|
+
return 'notepad';
|
|
510
|
+
}
|
|
511
|
+
else if (process.platform === 'darwin') {
|
|
512
|
+
return 'nano'; // Use nano on macOS as most users have it
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
return 'nano'; // Linux default to nano
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
async handleSetupEmail(args) {
|
|
519
|
+
const readline = require('readline');
|
|
520
|
+
const fs = require('fs');
|
|
521
|
+
const rl = readline.createInterface({
|
|
522
|
+
input: process.stdin,
|
|
523
|
+
output: process.stdout
|
|
524
|
+
});
|
|
525
|
+
const question = (prompt) => {
|
|
526
|
+
return new Promise((resolve) => {
|
|
527
|
+
rl.question(prompt, resolve);
|
|
528
|
+
});
|
|
529
|
+
};
|
|
530
|
+
try {
|
|
531
|
+
console.log('š CCGram Email Quick Setup Wizard\n');
|
|
532
|
+
// Select email provider
|
|
533
|
+
console.log('Please select your email provider:');
|
|
534
|
+
console.log('1. Gmail');
|
|
535
|
+
console.log('2. QQ Email');
|
|
536
|
+
console.log('3. 163 Email');
|
|
537
|
+
console.log('4. Outlook/Hotmail');
|
|
538
|
+
console.log('5. Custom');
|
|
539
|
+
const providerChoice = await question('\nPlease select (1-5): ');
|
|
540
|
+
let smtpHost = '', smtpPort = 587, imapHost = '', imapPort = 993, secure = false;
|
|
541
|
+
switch (providerChoice) {
|
|
542
|
+
case '1':
|
|
543
|
+
smtpHost = 'smtp.gmail.com';
|
|
544
|
+
smtpPort = 587;
|
|
545
|
+
imapHost = 'imap.gmail.com';
|
|
546
|
+
imapPort = 993;
|
|
547
|
+
secure = false;
|
|
548
|
+
console.log('\nš§ Gmail Configuration');
|
|
549
|
+
console.log('š” Need to enable two-factor authentication and generate app password first');
|
|
550
|
+
break;
|
|
551
|
+
case '2':
|
|
552
|
+
smtpHost = 'smtp.qq.com';
|
|
553
|
+
smtpPort = 587;
|
|
554
|
+
imapHost = 'imap.qq.com';
|
|
555
|
+
imapPort = 993;
|
|
556
|
+
secure = false;
|
|
557
|
+
console.log('\nš§ QQ Email Configuration');
|
|
558
|
+
break;
|
|
559
|
+
case '3':
|
|
560
|
+
smtpHost = 'smtp.163.com';
|
|
561
|
+
smtpPort = 587;
|
|
562
|
+
imapHost = 'imap.163.com';
|
|
563
|
+
imapPort = 993;
|
|
564
|
+
secure = false;
|
|
565
|
+
console.log('\nš§ 163 Email Configuration');
|
|
566
|
+
break;
|
|
567
|
+
case '4':
|
|
568
|
+
smtpHost = 'smtp.live.com';
|
|
569
|
+
smtpPort = 587;
|
|
570
|
+
imapHost = 'imap-mail.outlook.com';
|
|
571
|
+
imapPort = 993;
|
|
572
|
+
secure = false;
|
|
573
|
+
console.log('\nš§ Outlook Configuration');
|
|
574
|
+
break;
|
|
575
|
+
case '5':
|
|
576
|
+
console.log('\nš§ Custom Configuration');
|
|
577
|
+
smtpHost = await question('SMTP Host: ');
|
|
578
|
+
smtpPort = parseInt(await question('SMTP Port (default 587): ') || '587');
|
|
579
|
+
imapHost = await question('IMAP Host: ');
|
|
580
|
+
imapPort = parseInt(await question('IMAP Port (default 993): ') || '993');
|
|
581
|
+
const secureInput = await question('Use SSL/TLS? (y/n): ');
|
|
582
|
+
secure = secureInput.toLowerCase() === 'y';
|
|
583
|
+
break;
|
|
584
|
+
default:
|
|
585
|
+
console.log('ā Invalid selection');
|
|
586
|
+
rl.close();
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
// Get email account information
|
|
590
|
+
console.log('\nš Please enter email account information:');
|
|
591
|
+
const email = await question('Email address: ');
|
|
592
|
+
const password = await question('Password/App password: ');
|
|
593
|
+
// Build configuration
|
|
594
|
+
const emailConfig = {
|
|
595
|
+
type: "email",
|
|
596
|
+
enabled: true,
|
|
597
|
+
config: {
|
|
598
|
+
smtp: {
|
|
599
|
+
host: smtpHost,
|
|
600
|
+
port: smtpPort,
|
|
601
|
+
secure: secure,
|
|
602
|
+
auth: {
|
|
603
|
+
user: email,
|
|
604
|
+
pass: password
|
|
605
|
+
}
|
|
606
|
+
},
|
|
607
|
+
imap: {
|
|
608
|
+
host: imapHost,
|
|
609
|
+
port: imapPort,
|
|
610
|
+
secure: true,
|
|
611
|
+
auth: {
|
|
612
|
+
user: email,
|
|
613
|
+
pass: password
|
|
614
|
+
}
|
|
615
|
+
},
|
|
616
|
+
from: `CCGram <${email}>`,
|
|
617
|
+
to: email,
|
|
618
|
+
template: {
|
|
619
|
+
checkInterval: 30
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
// Read existing configuration
|
|
624
|
+
const channelsFile = path_1.default.join(paths_1.PROJECT_ROOT, 'config/channels.json');
|
|
625
|
+
let channels = {};
|
|
626
|
+
if (fs.existsSync(channelsFile)) {
|
|
627
|
+
channels = JSON.parse(fs.readFileSync(channelsFile, 'utf8'));
|
|
628
|
+
}
|
|
629
|
+
// Update email configuration
|
|
630
|
+
channels.email = emailConfig;
|
|
631
|
+
// Save configuration
|
|
632
|
+
fs.writeFileSync(channelsFile, JSON.stringify(channels, null, 2));
|
|
633
|
+
console.log('\nā
Email configuration saved!');
|
|
634
|
+
console.log('\nš§Ŗ You can now test email functionality:');
|
|
635
|
+
console.log(' claude-remote test');
|
|
636
|
+
console.log('\nš Start command relay service:');
|
|
637
|
+
console.log(' claude-remote relay start');
|
|
638
|
+
// Ask if user wants to test immediately
|
|
639
|
+
const testNow = await question('\nTest email sending now? (y/n): ');
|
|
640
|
+
if (testNow.toLowerCase() === 'y') {
|
|
641
|
+
rl.close();
|
|
642
|
+
// Reload configuration and test
|
|
643
|
+
await this.init();
|
|
644
|
+
await this.handleTest([]);
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
rl.close();
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
catch (error) {
|
|
651
|
+
console.error('ā Configuration failed:', error.message);
|
|
652
|
+
rl.close();
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
async handleDaemon(args) {
|
|
656
|
+
const ClaudeCodeRemoteDaemon = require('./src/daemon/taskping-daemon');
|
|
657
|
+
const daemon = new ClaudeCodeRemoteDaemon();
|
|
658
|
+
const command = args[0];
|
|
659
|
+
switch (command) {
|
|
660
|
+
case 'start':
|
|
661
|
+
await daemon.start();
|
|
662
|
+
break;
|
|
663
|
+
case 'stop':
|
|
664
|
+
await daemon.stop();
|
|
665
|
+
break;
|
|
666
|
+
case 'restart':
|
|
667
|
+
await daemon.restart();
|
|
668
|
+
break;
|
|
669
|
+
case 'status':
|
|
670
|
+
daemon.showStatus();
|
|
671
|
+
break;
|
|
672
|
+
default:
|
|
673
|
+
console.log('Usage: claude-remote daemon <start|stop|restart|status>');
|
|
674
|
+
console.log('');
|
|
675
|
+
console.log('Commands:');
|
|
676
|
+
console.log(' start Start background daemon process');
|
|
677
|
+
console.log(' stop Stop background daemon process');
|
|
678
|
+
console.log(' restart Restart background daemon process');
|
|
679
|
+
console.log(' status View daemon process status');
|
|
680
|
+
break;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
async handleCommands(args) {
|
|
684
|
+
const ClaudeCommandBridge = require('./src/relay/claude-command-bridge');
|
|
685
|
+
const bridge = new ClaudeCommandBridge();
|
|
686
|
+
const command = args[0];
|
|
687
|
+
switch (command) {
|
|
688
|
+
case 'list':
|
|
689
|
+
const pending = bridge.getPendingCommands();
|
|
690
|
+
console.log(`š Pending commands: ${pending.length}\n`);
|
|
691
|
+
if (pending.length > 0) {
|
|
692
|
+
pending.forEach((cmd, index) => {
|
|
693
|
+
console.log(`${index + 1}. ${cmd.id}`);
|
|
694
|
+
console.log(` Command: ${cmd.command}`);
|
|
695
|
+
console.log(` Time: ${cmd.timestamp}`);
|
|
696
|
+
console.log(` Session: ${cmd.sessionId}`);
|
|
697
|
+
console.log('');
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
break;
|
|
701
|
+
case 'status':
|
|
702
|
+
const status = bridge.getStatus();
|
|
703
|
+
console.log('š Command bridge status\n');
|
|
704
|
+
console.log(`Pending commands: ${status.pendingCommands}`);
|
|
705
|
+
console.log(`Processed commands: ${status.processedCommands}`);
|
|
706
|
+
console.log(`Commands directory: ${status.commandsDir}`);
|
|
707
|
+
console.log(`Response directory: ${status.responseDir}`);
|
|
708
|
+
if (status.recentCommands.length > 0) {
|
|
709
|
+
console.log('\nRecent commands:');
|
|
710
|
+
status.recentCommands.forEach((cmd) => {
|
|
711
|
+
console.log(` ⢠${cmd.command} (${cmd.timestamp})`);
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
break;
|
|
715
|
+
case 'cleanup':
|
|
716
|
+
bridge.cleanup();
|
|
717
|
+
console.log('š§¹ Old command files cleaned up');
|
|
718
|
+
break;
|
|
719
|
+
case 'clear':
|
|
720
|
+
const pending2 = bridge.getPendingCommands();
|
|
721
|
+
for (const cmd of pending2) {
|
|
722
|
+
bridge.markCommandProcessed(cmd.id, 'cancelled', 'Manually cancelled');
|
|
723
|
+
}
|
|
724
|
+
console.log(`šļø Cleared ${pending2.length} pending commands`);
|
|
725
|
+
break;
|
|
726
|
+
default:
|
|
727
|
+
console.log('Usage: claude-remote commands <list|status|cleanup|clear>');
|
|
728
|
+
console.log('');
|
|
729
|
+
console.log('Commands:');
|
|
730
|
+
console.log(' list Show pending email commands');
|
|
731
|
+
console.log(' status Show command bridge status');
|
|
732
|
+
console.log(' cleanup Clean up old command files');
|
|
733
|
+
console.log(' clear Clear all pending commands');
|
|
734
|
+
break;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
async handleTestPaste(args) {
|
|
738
|
+
const ClipboardAutomation = require('./src/automation/clipboard-automation');
|
|
739
|
+
const automation = new ClipboardAutomation();
|
|
740
|
+
const testCommand = args.join(' ') || 'echo "Testing email reply auto-paste functionality"';
|
|
741
|
+
console.log('š§Ŗ Testing auto-paste functionality');
|
|
742
|
+
console.log(`š Test command: ${testCommand}`);
|
|
743
|
+
console.log('\nā ļø Please ensure Claude Code or Terminal window is open and active');
|
|
744
|
+
console.log('ā³ Command will be sent automatically in 3 seconds...\n');
|
|
745
|
+
// Countdown
|
|
746
|
+
for (let i = 3; i > 0; i--) {
|
|
747
|
+
process.stdout.write(`${i}... `);
|
|
748
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
749
|
+
}
|
|
750
|
+
console.log('\n');
|
|
751
|
+
try {
|
|
752
|
+
const success = await automation.sendCommand(testCommand);
|
|
753
|
+
if (success) {
|
|
754
|
+
console.log('ā
Command has been auto-pasted!');
|
|
755
|
+
console.log('š” If you don\'t see the effect, please check app permissions and window status');
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
console.log('ā Auto-paste failed');
|
|
759
|
+
console.log('š” Please ensure automation permissions are granted and target app is open');
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
catch (error) {
|
|
763
|
+
console.error('ā Test failed:', error.message);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
async handleSetupPermissions(args) {
|
|
767
|
+
const PermissionSetup = require('./setup-permissions');
|
|
768
|
+
const setup = new PermissionSetup();
|
|
769
|
+
await setup.checkAndSetup();
|
|
770
|
+
}
|
|
771
|
+
async handleTestSimple(args) {
|
|
772
|
+
const SimpleAutomation = require('./src/automation/simple-automation');
|
|
773
|
+
const automation = new SimpleAutomation();
|
|
774
|
+
const testCommand = args.join(' ') || 'echo "Testing simple automation functionality"';
|
|
775
|
+
console.log('š§Ŗ Testing simple automation functionality');
|
|
776
|
+
console.log(`š Test command: ${testCommand}`);
|
|
777
|
+
console.log('\nThis test will:');
|
|
778
|
+
console.log('1. š Copy command to clipboard');
|
|
779
|
+
console.log('2. š Save command to file');
|
|
780
|
+
console.log('3. š Send notification (including dialog box)');
|
|
781
|
+
console.log('4. š¤ Attempt auto-paste (if permissions granted)');
|
|
782
|
+
console.log('\nā³ Starting test...\n');
|
|
783
|
+
try {
|
|
784
|
+
const success = await automation.sendCommand(testCommand, 'test-session');
|
|
785
|
+
if (success) {
|
|
786
|
+
console.log('ā
Test successful!');
|
|
787
|
+
console.log('\nš Next steps:');
|
|
788
|
+
console.log('1. Check if you received notification');
|
|
789
|
+
console.log('2. Check if command was copied to clipboard');
|
|
790
|
+
console.log('3. If you see dialog box, you can choose to open command file');
|
|
791
|
+
console.log('4. Manually paste to Claude Code (if auto-paste didn\'t work)');
|
|
792
|
+
const status = automation.getStatus();
|
|
793
|
+
console.log(`\nš Command file: ${status.commandFile}`);
|
|
794
|
+
if (status.commandFileExists) {
|
|
795
|
+
console.log('š” You can run "open -t ' + status.commandFile + '" to view command file');
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
else {
|
|
799
|
+
console.log('ā Test failed');
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
catch (error) {
|
|
803
|
+
console.error('ā Error occurred during test:', error.message);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
async handleTestClaude(args) {
|
|
807
|
+
const ClaudeAutomation = require('./src/automation/claude-automation');
|
|
808
|
+
const automation = new ClaudeAutomation();
|
|
809
|
+
const testCommand = args.join(' ') || 'echo "This is an automated test command from email reply"';
|
|
810
|
+
console.log('š¤ Testing Claude Code specialized automation');
|
|
811
|
+
console.log(`š Test command: ${testCommand}`);
|
|
812
|
+
console.log('\nā ļø Please ensure:');
|
|
813
|
+
console.log(' 1. Claude Code application is open');
|
|
814
|
+
console.log(' 2. Or Terminal/iTerm2 etc. terminal applications are open');
|
|
815
|
+
console.log(' 3. Necessary accessibility permissions have been granted');
|
|
816
|
+
console.log('\nā³ Full automation test will start in 5 seconds...\n');
|
|
817
|
+
// Countdown
|
|
818
|
+
for (let i = 5; i > 0; i--) {
|
|
819
|
+
process.stdout.write(`${i}... `);
|
|
820
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
821
|
+
}
|
|
822
|
+
console.log('\nš Starting automation...\n');
|
|
823
|
+
try {
|
|
824
|
+
// Check permissions
|
|
825
|
+
const hasPermission = await automation.requestPermissions();
|
|
826
|
+
if (!hasPermission) {
|
|
827
|
+
console.log('ā ļø Permission check failed, but will still attempt execution...');
|
|
828
|
+
}
|
|
829
|
+
// Execute full automation
|
|
830
|
+
const success = await automation.sendCommand(testCommand, 'test-session');
|
|
831
|
+
if (success) {
|
|
832
|
+
console.log('ā
Full automation test successful!');
|
|
833
|
+
console.log('š” Command should have been automatically input to Claude Code and started execution');
|
|
834
|
+
console.log('š Please check Claude Code window to see if command was received');
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
console.log('ā Automation test failed');
|
|
838
|
+
console.log('š” Possible reasons:');
|
|
839
|
+
console.log(' ⢠Claude Code or terminal application not found');
|
|
840
|
+
console.log(' ⢠Insufficient permissions');
|
|
841
|
+
console.log(' ⢠Application not responding');
|
|
842
|
+
console.log('\nš§ Suggestions:');
|
|
843
|
+
console.log(' 1. Run "claude-remote setup-permissions" to check permissions');
|
|
844
|
+
console.log(' 2. Ensure Claude Code is running in foreground');
|
|
845
|
+
console.log(' 3. Try manually clicking input box in Claude Code first');
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
catch (error) {
|
|
849
|
+
console.error('ā Error occurred during test:', error.message);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
async handleDiagnose(args) {
|
|
853
|
+
const AutomationDiagnostic = require('./diagnose-automation');
|
|
854
|
+
const diagnostic = new AutomationDiagnostic();
|
|
855
|
+
await diagnostic.runDiagnostic();
|
|
856
|
+
}
|
|
857
|
+
showHelp() {
|
|
858
|
+
console.log(`
|
|
859
|
+
CCGram - Claude Code Smart Notification System
|
|
860
|
+
|
|
861
|
+
Usage: claude-remote <command> [options]
|
|
862
|
+
|
|
863
|
+
Commands:
|
|
864
|
+
notify --type <type> Send a notification (completed|waiting)
|
|
865
|
+
test Test all notification channels
|
|
866
|
+
status Show system status
|
|
867
|
+
config Launch configuration manager
|
|
868
|
+
setup-email Quick email setup wizard
|
|
869
|
+
edit-config <type> Edit configuration files directly
|
|
870
|
+
install Install and configure Claude Code hooks
|
|
871
|
+
relay <subcommand> Manage email command relay service
|
|
872
|
+
daemon <subcommand> Manage background daemon service
|
|
873
|
+
commands <subcommand> Manage email commands and bridge
|
|
874
|
+
test-paste [command] Test automatic paste functionality
|
|
875
|
+
test-simple [command] Test simple automation (recommended)
|
|
876
|
+
test-claude [command] Test Claude Code full automation
|
|
877
|
+
setup-permissions Setup macOS permissions for automation
|
|
878
|
+
diagnose Diagnose automation issues
|
|
879
|
+
|
|
880
|
+
Options:
|
|
881
|
+
-h, --help Show this help message
|
|
882
|
+
|
|
883
|
+
Relay Subcommands:
|
|
884
|
+
relay start Start email command relay service
|
|
885
|
+
relay stop Stop email command relay service
|
|
886
|
+
relay status Show relay service status
|
|
887
|
+
relay cleanup Clean up completed command history
|
|
888
|
+
|
|
889
|
+
Daemon Subcommands:
|
|
890
|
+
daemon start Start background daemon service
|
|
891
|
+
daemon stop Stop background daemon service
|
|
892
|
+
daemon restart Restart background daemon service
|
|
893
|
+
daemon status Show daemon service status
|
|
894
|
+
|
|
895
|
+
Commands Subcommands:
|
|
896
|
+
commands list Show pending email commands
|
|
897
|
+
commands status Show command bridge status
|
|
898
|
+
commands cleanup Clean up old command files
|
|
899
|
+
commands clear Clear all pending commands
|
|
900
|
+
|
|
901
|
+
Examples:
|
|
902
|
+
claude-remote notify --type completed
|
|
903
|
+
claude-remote test
|
|
904
|
+
claude-remote setup-email # Quick email setup (recommended)
|
|
905
|
+
claude-remote edit-config channels # Edit configuration files directly
|
|
906
|
+
claude-remote config # Interactive configuration
|
|
907
|
+
claude-remote install
|
|
908
|
+
claude-remote daemon start # Start background service (recommended)
|
|
909
|
+
claude-remote daemon status # View service status
|
|
910
|
+
claude-remote test-claude # Test full automation (recommended)
|
|
911
|
+
claude-remote commands list # View pending email commands
|
|
912
|
+
claude-remote relay start # Run in foreground (need to keep window open)
|
|
913
|
+
|
|
914
|
+
For more information, visit: https://github.com/CCGram/CCGram
|
|
915
|
+
`);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
// Run CLI if this file is executed directly
|
|
919
|
+
if (require.main === module) {
|
|
920
|
+
const cli = new ClaudeCodeRemoteCLI();
|
|
921
|
+
cli.run().catch((error) => {
|
|
922
|
+
console.error('Fatal error:', error.message);
|
|
923
|
+
process.exit(1);
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
module.exports = ClaudeCodeRemoteCLI;
|
|
927
|
+
//# sourceMappingURL=claude-remote.js.map
|