@adiontaegerron/claude-multi-terminal 2.0.0 → 2.1.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/.claude/settings.local.json +8 -15
- package/CLAUDE.md +75 -42
- package/README.md +20 -0
- package/ipc-handlers.js +303 -0
- package/main.js +441 -25
- package/package.json +6 -3
- package/renderer.js +364 -3
- package/test/test.html +12 -0
- package/test-app.js +0 -16
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
|
-
"Bash(
|
|
5
|
-
"Bash(
|
|
6
|
-
"Bash(
|
|
7
|
-
"Bash(npm start)",
|
|
8
|
-
"Bash(npx:*)",
|
|
4
|
+
"Bash(create_terminal \"Terminal 3\")",
|
|
5
|
+
"Bash(terminal_help)",
|
|
6
|
+
"Bash(create_terminal \"Terminal 4\")",
|
|
9
7
|
"Bash(rm:*)",
|
|
10
|
-
"Bash(
|
|
11
|
-
"Bash(git init:*)",
|
|
12
|
-
"Bash(git remote add:*)",
|
|
13
|
-
"Bash(git add:*)",
|
|
14
|
-
"Bash(git commit:*)",
|
|
15
|
-
"Bash(git push:*)",
|
|
16
|
-
"Bash(mkdir:*)",
|
|
8
|
+
"Bash(create_terminal \"Terminal 2\")",
|
|
17
9
|
"Bash(chmod:*)",
|
|
18
|
-
"Bash(
|
|
19
|
-
"Bash(
|
|
20
|
-
"
|
|
10
|
+
"Bash(*)",
|
|
11
|
+
"Bash(/tmp/create_terminals.sh:*)",
|
|
12
|
+
"Bash(ls:*)",
|
|
13
|
+
"Write(*)"
|
|
21
14
|
],
|
|
22
15
|
"deny": []
|
|
23
16
|
}
|
package/CLAUDE.md
CHANGED
|
@@ -1,63 +1,96 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Terminal Agent Instructions
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
You are a terminal agent running in a multi-terminal coordination system. Your terminal name will be provided when you start.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## IMPORTANT: Follow Instructions Exactly
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
When given a command:
|
|
8
|
+
- Execute ONLY what is explicitly requested
|
|
9
|
+
- Do NOT add extra actions or create additional items
|
|
10
|
+
- If asked to create 2 terminals, create exactly 2 (not 3, 4, or 6)
|
|
11
|
+
- Wait for further instructions after completing each task
|
|
8
12
|
|
|
9
|
-
##
|
|
13
|
+
## Communication Commands
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
You communicate with other terminals using file-based IPC. Use the Write tool to create command files in `.ipc-messages/`.
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
# Install dependencies
|
|
15
|
-
npm install
|
|
17
|
+
### Command Format
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
Use the Write tool to create files with unique names. Include your name in the filename to ensure uniqueness:
|
|
20
|
+
`.ipc-messages/cmd_YourName_timestamp.txt`
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
npm run build
|
|
22
|
+
Always use the exact terminal name you were given at startup.
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
### Send Message to Another Terminal
|
|
25
|
+
Use Write to create a file `.ipc-messages/msg_YourName_[timestamp].txt` with content:
|
|
26
|
+
```
|
|
27
|
+
YourName:IPC:SEND:TargetTerminal:Your message
|
|
25
28
|
```
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
### Broadcast to All Terminals
|
|
31
|
+
Use Write to create a file `.ipc-messages/bcast_YourName_[timestamp].txt` with content:
|
|
32
|
+
```
|
|
33
|
+
YourName:IPC:BROADCAST:Your message
|
|
34
|
+
```
|
|
28
35
|
|
|
29
|
-
|
|
36
|
+
### List All Terminals
|
|
37
|
+
Use Write to create a file `.ipc-messages/list_YourName_[timestamp].txt` with content:
|
|
38
|
+
```
|
|
39
|
+
YourName:IPC:LIST
|
|
40
|
+
```
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
### Create New Terminal
|
|
43
|
+
Use Write to create a file `.ipc-messages/create_YourName_[timestamp].txt` with content:
|
|
44
|
+
```
|
|
45
|
+
YourName:IPC:CREATE:NewTerminalName
|
|
46
|
+
```
|
|
47
|
+
The name is optional - if not provided, a default name will be assigned.
|
|
34
48
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
### Close Terminal
|
|
50
|
+
Use Write to create a file `.ipc-messages/close_YourName_[timestamp].txt` with content:
|
|
51
|
+
```
|
|
52
|
+
YourName:IPC:CLOSE:TargetTerminal
|
|
53
|
+
```
|
|
54
|
+
Note: You cannot close your own terminal.
|
|
55
|
+
|
|
56
|
+
### Rename Terminal
|
|
57
|
+
Use Write to create a file `.ipc-messages/rename_YourName_[timestamp].txt` with content:
|
|
58
|
+
```
|
|
59
|
+
YourName:IPC:RENAME:TargetTerminal:NewName
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Get Terminal Status
|
|
63
|
+
Use Write to create a file `.ipc-messages/status_YourName_[timestamp].txt` with content:
|
|
64
|
+
```
|
|
65
|
+
YourName:IPC:STATUS:TargetTerminal
|
|
66
|
+
```
|
|
39
67
|
|
|
40
|
-
|
|
68
|
+
**Important**: Replace [timestamp] with a unique number or use the current timestamp when creating files.
|
|
41
69
|
|
|
42
|
-
|
|
43
|
-
- Multiple terminal sessions (2+ terminals)
|
|
44
|
-
- Each terminal displays:
|
|
45
|
-
- xterm.js terminal area for output and keyboard input
|
|
46
|
-
- Text input box for sending commands
|
|
47
|
-
- Send button (Enter key also submits)
|
|
48
|
-
- "New Terminal" button for creating sessions
|
|
49
|
-
- Real-time output display
|
|
50
|
-
- Local execution only (no cloud dependencies)
|
|
70
|
+
## Important Rules
|
|
51
71
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
72
|
+
1. Terminal names are case-sensitive
|
|
73
|
+
2. You cannot send messages to yourself
|
|
74
|
+
3. You cannot close your own terminal
|
|
75
|
+
4. Maximum of 6 terminals can exist
|
|
76
|
+
5. Messages are delivered instantly
|
|
77
|
+
6. When you receive a message, it will appear as:
|
|
78
|
+
- `💬 Message from Terminal X: [message]`
|
|
79
|
+
- `📢 Broadcast from Terminal X: [message]`
|
|
55
80
|
|
|
56
|
-
##
|
|
81
|
+
## Your Role
|
|
57
82
|
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
83
|
+
As a terminal agent, you should:
|
|
84
|
+
- Wait for messages and commands from other terminals
|
|
85
|
+
- When you receive a message ending with "[End of message - no response needed]", do NOT respond automatically
|
|
86
|
+
- Only respond to messages when they contain a specific request or question
|
|
87
|
+
- Execute tasks as directed
|
|
88
|
+
- Communicate status updates when appropriate
|
|
89
|
+
- Avoid taking actions unless specifically requested
|
|
62
90
|
|
|
91
|
+
## Important: Message Handling
|
|
63
92
|
|
|
93
|
+
When you receive messages from other terminals:
|
|
94
|
+
- Messages marked with "[End of message - no response needed]" are notifications only
|
|
95
|
+
- Do not echo or respond to these messages unless they contain a specific request
|
|
96
|
+
- Wait for explicit instructions before taking action
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Claude Multi-Terminal
|
|
2
2
|
|
|
3
|
+
**Note for Terminal Agents**: If you are a Claude instance running inside this application, you don't need to read this file. Please refer to CLAUDE.md for your instructions.
|
|
4
|
+
|
|
3
5
|
A multi-terminal editor for coordinating multiple Claude Code instances. Run multiple Claude sessions side-by-side with a unified chat interface to send commands to selected terminals.
|
|
4
6
|
|
|
5
7
|

|
|
@@ -21,6 +23,8 @@ A multi-terminal editor for coordinating multiple Claude Code instances. Run mul
|
|
|
21
23
|
- **Default Prompt**: Set a default prompt to prepend to all messages
|
|
22
24
|
- **Plan Mode Support**: Automatically enters plan mode with Shift+Tab
|
|
23
25
|
- **Auto-Start Claude**: Each terminal automatically starts Claude Code
|
|
26
|
+
- **Inter-Claude Communication**: Claude instances can chat and coordinate using echo commands
|
|
27
|
+
- **Terminal Management**: Claude can create, close, and rename terminals autonomously
|
|
24
28
|
- **Rename Terminals**: Double-click terminal titles to rename them
|
|
25
29
|
- **Responsive Layout**: Adapts to different screen sizes
|
|
26
30
|
|
|
@@ -94,13 +98,29 @@ Send commands directly to terminals using @ syntax:
|
|
|
94
98
|
|
|
95
99
|
**Autocomplete**: Type `@` anywhere to get terminal suggestions!
|
|
96
100
|
|
|
101
|
+
### Claude-to-Claude Communication
|
|
102
|
+
|
|
103
|
+
Claude instances communicate by creating files in the `.ipc-messages/` directory. Claude will handle the file creation directly when asked to:
|
|
104
|
+
|
|
105
|
+
- **Send message**: Claude creates a file to send `TerminalName:IPC:SEND:Target:Message`
|
|
106
|
+
- **Broadcast**: Claude creates a file to broadcast `TerminalName:IPC:BROADCAST:Message`
|
|
107
|
+
- **Create terminal**: Claude creates a file with `TerminalName:IPC:CREATE:NewName`
|
|
108
|
+
- **Close terminal**: Claude creates a file with `TerminalName:IPC:CLOSE:Target`
|
|
109
|
+
- **Rename terminal**: Claude creates a file with `TerminalName:IPC:RENAME:Target:NewName`
|
|
110
|
+
- **List terminals**: Claude creates a file with `TerminalName:IPC:LIST`
|
|
111
|
+
- **Get status**: Claude creates a file with `TerminalName:IPC:STATUS:Target`
|
|
112
|
+
|
|
113
|
+
Claude knows how to create these files when you ask it to communicate with other terminals. This enables Claude instances to work together as a coordinated team.
|
|
114
|
+
|
|
97
115
|
### Tips
|
|
98
116
|
|
|
99
117
|
- Each terminal automatically starts Claude Code when created
|
|
118
|
+
- Claude receives its identity: "You are a terminal agent. Your name is [Terminal Name]."
|
|
100
119
|
- Plan mode is activated automatically using Shift+Tab
|
|
101
120
|
- Double-click terminal titles to rename them for better organization
|
|
102
121
|
- The application uses your current directory as the working directory for all terminals
|
|
103
122
|
- Use multi-@ commands to coordinate multiple Claude instances efficiently
|
|
123
|
+
- Claude instances communicate instantly using IPC echo commands for seamless teamwork
|
|
104
124
|
|
|
105
125
|
## Development
|
|
106
126
|
|
package/ipc-handlers.js
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
// IPC Handlers for Direct Terminal Communication
|
|
2
|
+
// Handles IPC calls from injected JavaScript functions
|
|
3
|
+
|
|
4
|
+
const { ipcMain } = require('electron');
|
|
5
|
+
|
|
6
|
+
class DirectIPCHandler {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.terminals = null; // Will be set by main process
|
|
9
|
+
this.mainWindow = null; // Will be set by main process
|
|
10
|
+
this.setupHandlers();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
setTerminals(terminals) {
|
|
14
|
+
this.terminals = terminals;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
setMainWindow(mainWindow) {
|
|
18
|
+
this.mainWindow = mainWindow;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setupHandlers() {
|
|
22
|
+
// Send message between terminals
|
|
23
|
+
ipcMain.handle('terminal:send-message', async (event, data) => {
|
|
24
|
+
try {
|
|
25
|
+
const { from, to, message, timestamp } = data;
|
|
26
|
+
|
|
27
|
+
const targetTerminal = this.findTerminal(to);
|
|
28
|
+
if (!targetTerminal) {
|
|
29
|
+
return { success: false, error: `Terminal '${to}' not found` };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const chatMessage = `💬 Message from ${from}:\n${message}`;
|
|
33
|
+
this.mainWindow.webContents.send('terminal:system-response', targetTerminal.id, {
|
|
34
|
+
message: chatMessage,
|
|
35
|
+
type: 'chat',
|
|
36
|
+
timestamp: timestamp || Date.now()
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Send return to submit message
|
|
40
|
+
setTimeout(() => {
|
|
41
|
+
targetTerminal.pty.write('\r');
|
|
42
|
+
}, 200);
|
|
43
|
+
|
|
44
|
+
return { success: true, target: to, from };
|
|
45
|
+
} catch (error) {
|
|
46
|
+
return { success: false, error: error.message };
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Create new terminal
|
|
51
|
+
ipcMain.handle('terminal:create-new', async (event, data) => {
|
|
52
|
+
try {
|
|
53
|
+
const { name, requesterId } = data;
|
|
54
|
+
|
|
55
|
+
// Check terminal limit
|
|
56
|
+
if (this.terminals.size >= 6) {
|
|
57
|
+
return { success: false, error: 'Maximum of 6 terminals allowed' };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Rate limiting check
|
|
61
|
+
const requesterTerminal = this.terminals.get(requesterId);
|
|
62
|
+
if (requesterTerminal) {
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
const timeSinceLastCreate = now - (requesterTerminal.lastCreateRequest || 0);
|
|
65
|
+
if (timeSinceLastCreate < 2000) {
|
|
66
|
+
return {
|
|
67
|
+
success: false,
|
|
68
|
+
error: `Please wait ${Math.ceil((2000 - timeSinceLastCreate) / 1000)} seconds before creating another terminal`
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
requesterTerminal.lastCreateRequest = now;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Trigger terminal creation
|
|
75
|
+
this.mainWindow.webContents.send('terminal:create-request', name);
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
success: true,
|
|
79
|
+
terminalId: `terminal-${Date.now()}`, // Temporary ID
|
|
80
|
+
name: name
|
|
81
|
+
};
|
|
82
|
+
} catch (error) {
|
|
83
|
+
return { success: false, error: error.message };
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Close terminal
|
|
88
|
+
ipcMain.handle('terminal:close-target', async (event, data) => {
|
|
89
|
+
try {
|
|
90
|
+
const { target, requesterId } = data;
|
|
91
|
+
|
|
92
|
+
const targetTerminal = this.findTerminal(target);
|
|
93
|
+
if (!targetTerminal) {
|
|
94
|
+
return { success: false, error: `Terminal '${target}' not found` };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (targetTerminal.id === requesterId) {
|
|
98
|
+
return { success: false, error: 'Cannot close your own terminal' };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
targetTerminal.pty.kill();
|
|
102
|
+
this.terminals.delete(targetTerminal.id);
|
|
103
|
+
|
|
104
|
+
this.mainWindow.webContents.send('terminal:closed', targetTerminal.id);
|
|
105
|
+
|
|
106
|
+
return { success: true, target, closed: targetTerminal.name };
|
|
107
|
+
} catch (error) {
|
|
108
|
+
return { success: false, error: error.message };
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Rename terminal
|
|
113
|
+
ipcMain.handle('terminal:rename-target', async (event, data) => {
|
|
114
|
+
try {
|
|
115
|
+
const { target, newName, requesterId } = data;
|
|
116
|
+
|
|
117
|
+
const targetTerminal = this.findTerminal(target);
|
|
118
|
+
if (!targetTerminal) {
|
|
119
|
+
return { success: false, error: `Terminal '${target}' not found` };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const oldName = targetTerminal.name;
|
|
123
|
+
targetTerminal.name = newName;
|
|
124
|
+
|
|
125
|
+
this.mainWindow.webContents.send('terminal:renamed', targetTerminal.id, newName);
|
|
126
|
+
|
|
127
|
+
return { success: true, target, oldName, newName };
|
|
128
|
+
} catch (error) {
|
|
129
|
+
return { success: false, error: error.message };
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// List all terminals
|
|
134
|
+
ipcMain.handle('terminal:list-all', async (event) => {
|
|
135
|
+
try {
|
|
136
|
+
const terminalList = Array.from(this.terminals.entries()).map(([id, terminal]) => ({
|
|
137
|
+
id,
|
|
138
|
+
name: terminal.name,
|
|
139
|
+
status: terminal.status || 'active',
|
|
140
|
+
hasClaudeStarted: terminal.hasClaudeStarted || false
|
|
141
|
+
}));
|
|
142
|
+
|
|
143
|
+
return { success: true, terminals: terminalList };
|
|
144
|
+
} catch (error) {
|
|
145
|
+
return { success: false, error: error.message };
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Get terminal status
|
|
150
|
+
ipcMain.handle('terminal:get-status', async (event, data) => {
|
|
151
|
+
try {
|
|
152
|
+
const { target } = data;
|
|
153
|
+
|
|
154
|
+
const targetTerminal = this.findTerminal(target);
|
|
155
|
+
if (!targetTerminal) {
|
|
156
|
+
return { success: false, error: `Terminal '${target}' not found` };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const status = {
|
|
160
|
+
id: targetTerminal.id,
|
|
161
|
+
name: targetTerminal.name,
|
|
162
|
+
status: targetTerminal.status || 'active',
|
|
163
|
+
hasClaudeStarted: targetTerminal.hasClaudeStarted || false
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
return { success: true, target, status };
|
|
167
|
+
} catch (error) {
|
|
168
|
+
return { success: false, error: error.message };
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Broadcast message to all terminals
|
|
173
|
+
ipcMain.handle('terminal:broadcast', async (event, data) => {
|
|
174
|
+
try {
|
|
175
|
+
const { message, from, excludeId } = data;
|
|
176
|
+
|
|
177
|
+
let count = 0;
|
|
178
|
+
for (const [id, terminal] of this.terminals.entries()) {
|
|
179
|
+
if (excludeId && id === excludeId) continue; // Don't send to sender
|
|
180
|
+
|
|
181
|
+
const chatMessage = `📢 Broadcast from ${from}:\n${message}`;
|
|
182
|
+
this.mainWindow.webContents.send('terminal:system-response', id, {
|
|
183
|
+
message: chatMessage,
|
|
184
|
+
type: 'chat',
|
|
185
|
+
timestamp: Date.now()
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
count++;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return { success: true, count, message };
|
|
192
|
+
} catch (error) {
|
|
193
|
+
return { success: false, error: error.message };
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Advanced workflow handlers
|
|
198
|
+
ipcMain.handle('terminal:workflow-setup', async (event, data) => {
|
|
199
|
+
try {
|
|
200
|
+
const { type, requesterId } = data;
|
|
201
|
+
|
|
202
|
+
const workflows = {
|
|
203
|
+
'development': [
|
|
204
|
+
'Backend Server',
|
|
205
|
+
'Frontend Dev',
|
|
206
|
+
'Database',
|
|
207
|
+
'Test Runner'
|
|
208
|
+
],
|
|
209
|
+
'review': [
|
|
210
|
+
'Code Reviewer',
|
|
211
|
+
'Test Runner',
|
|
212
|
+
'Build Monitor'
|
|
213
|
+
],
|
|
214
|
+
'debug': [
|
|
215
|
+
'Bug Hunter',
|
|
216
|
+
'Log Monitor',
|
|
217
|
+
'Test Verifier'
|
|
218
|
+
]
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const terminalNames = workflows[type];
|
|
222
|
+
if (!terminalNames) {
|
|
223
|
+
return { success: false, error: `Unknown workflow type: ${type}` };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Check if we can create all terminals
|
|
227
|
+
if (this.terminals.size + terminalNames.length > 6) {
|
|
228
|
+
return {
|
|
229
|
+
success: false,
|
|
230
|
+
error: `Cannot create ${terminalNames.length} terminals. Would exceed limit of 6.`
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Create terminals
|
|
235
|
+
const created = [];
|
|
236
|
+
for (const name of terminalNames) {
|
|
237
|
+
this.mainWindow.webContents.send('terminal:create-request', name);
|
|
238
|
+
created.push(name);
|
|
239
|
+
// Small delay between creations
|
|
240
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return { success: true, type, created };
|
|
244
|
+
} catch (error) {
|
|
245
|
+
return { success: false, error: error.message };
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Health check
|
|
250
|
+
ipcMain.handle('terminal:health', async (event) => {
|
|
251
|
+
try {
|
|
252
|
+
return {
|
|
253
|
+
success: true,
|
|
254
|
+
terminalCount: this.terminals.size,
|
|
255
|
+
uptime: process.uptime(),
|
|
256
|
+
platform: process.platform,
|
|
257
|
+
memory: process.memoryUsage()
|
|
258
|
+
};
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return { success: false, error: error.message };
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Helper method to find terminals
|
|
266
|
+
findTerminal(identifier) {
|
|
267
|
+
if (!this.terminals) {
|
|
268
|
+
throw new Error('Terminals not initialized');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Try by ID first
|
|
272
|
+
if (this.terminals.has(identifier)) {
|
|
273
|
+
return this.terminals.get(identifier);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Try by name (exact match)
|
|
277
|
+
for (const [id, terminal] of this.terminals.entries()) {
|
|
278
|
+
if (terminal.name === identifier) {
|
|
279
|
+
return terminal;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Try by name (partial match)
|
|
284
|
+
for (const [id, terminal] of this.terminals.entries()) {
|
|
285
|
+
if (terminal.name.toLowerCase().includes(identifier.toLowerCase())) {
|
|
286
|
+
return terminal;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Try by position
|
|
291
|
+
const position = parseInt(identifier);
|
|
292
|
+
if (!isNaN(position) && position > 0) {
|
|
293
|
+
const terminalArray = Array.from(this.terminals.values());
|
|
294
|
+
if (position <= terminalArray.length) {
|
|
295
|
+
return terminalArray[position - 1];
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
module.exports = DirectIPCHandler;
|