agent-gear 1.1.2 → 1.1.4
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/AGENTS.md +23 -0
- package/dist/acp/detector.d.ts +3 -0
- package/dist/acp/detector.d.ts.map +1 -1
- package/dist/acp/detector.js +41 -2
- package/dist/acp/detector.js.map +1 -1
- package/dist/{port-forward.d.ts → gateway.d.ts} +7 -3
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +158 -0
- package/dist/gateway.js.map +1 -0
- package/dist/gateway.test.d.ts +2 -0
- package/dist/gateway.test.d.ts.map +1 -0
- package/dist/gateway.test.js +241 -0
- package/dist/gateway.test.js.map +1 -0
- package/dist/socketio-client.d.ts.map +1 -1
- package/dist/socketio-client.js +16 -12
- package/dist/socketio-client.js.map +1 -1
- package/package.json +1 -1
- package/src/acp/detector.ts +41 -2
- package/src/gateway.test.ts +301 -0
- package/src/gateway.ts +195 -0
- package/src/socketio-client.ts +18 -12
- package/tests/acp-detector.test.ts +68 -26
- package/dist/port-forward.d.ts.map +0 -1
- package/dist/port-forward.js +0 -68
- package/dist/port-forward.js.map +0 -1
- package/dist/port-forward.test.d.ts +0 -2
- package/dist/port-forward.test.d.ts.map +0 -1
- package/dist/port-forward.test.js +0 -163
- package/dist/port-forward.test.js.map +0 -1
- package/src/port-forward.test.ts +0 -202
- package/src/port-forward.ts +0 -88
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
vi.mock('node:net', () => ({
|
|
3
|
-
default: {
|
|
4
|
-
connect: vi.fn(),
|
|
5
|
-
},
|
|
6
|
-
}));
|
|
7
|
-
import net from 'node:net';
|
|
8
|
-
import { PortForwardManager } from './port-forward.js';
|
|
9
|
-
function createMockLocalSocket() {
|
|
10
|
-
const handlers = new Map();
|
|
11
|
-
return {
|
|
12
|
-
once: vi.fn((event, handler) => {
|
|
13
|
-
handlers.set(event, handler);
|
|
14
|
-
}),
|
|
15
|
-
on: vi.fn((event, handler) => {
|
|
16
|
-
handlers.set(event, handler);
|
|
17
|
-
}),
|
|
18
|
-
removeListener: vi.fn(),
|
|
19
|
-
destroy: vi.fn(),
|
|
20
|
-
write: vi.fn(),
|
|
21
|
-
emit: vi.fn(),
|
|
22
|
-
_handlers: handlers,
|
|
23
|
-
_trigger(event, ...args) {
|
|
24
|
-
const handler = handlers.get(event);
|
|
25
|
-
if (handler)
|
|
26
|
-
handler(...args);
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
function createMockServerSocket() {
|
|
31
|
-
return {
|
|
32
|
-
emit: vi.fn(),
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
describe('PortForwardManager', () => {
|
|
36
|
-
let portForwardManager;
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
vi.clearAllMocks();
|
|
39
|
-
portForwardManager = new PortForwardManager();
|
|
40
|
-
});
|
|
41
|
-
describe('createTunnel', () => {
|
|
42
|
-
it('should send port-forward:created(success: true) and store tunnel on connection success', async () => {
|
|
43
|
-
const mockLocalSocket = createMockLocalSocket();
|
|
44
|
-
const mockServerSocket = createMockServerSocket();
|
|
45
|
-
vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
|
|
46
|
-
const promise = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket);
|
|
47
|
-
mockLocalSocket._trigger('connect');
|
|
48
|
-
await promise;
|
|
49
|
-
expect(net.connect).toHaveBeenCalledWith({ host: '127.0.0.1', port: 3000 });
|
|
50
|
-
expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:created', {
|
|
51
|
-
tunnelId: 'tunnel-1',
|
|
52
|
-
success: true,
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
it('should send port-forward:created(success: false, error) and destroy socket on connection failure', async () => {
|
|
56
|
-
const mockLocalSocket = createMockLocalSocket();
|
|
57
|
-
const mockServerSocket = createMockServerSocket();
|
|
58
|
-
vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
|
|
59
|
-
const promise = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket);
|
|
60
|
-
mockLocalSocket._trigger('error', new Error('ECONNREFUSED'));
|
|
61
|
-
await promise;
|
|
62
|
-
expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:created', {
|
|
63
|
-
tunnelId: 'tunnel-1',
|
|
64
|
-
success: false,
|
|
65
|
-
error: 'ECONNREFUSED',
|
|
66
|
-
});
|
|
67
|
-
expect(mockLocalSocket.destroy).toHaveBeenCalled();
|
|
68
|
-
});
|
|
69
|
-
it('should send port-forward:data with base64 encoded data when local socket receives data', async () => {
|
|
70
|
-
const mockLocalSocket = createMockLocalSocket();
|
|
71
|
-
const mockServerSocket = createMockServerSocket();
|
|
72
|
-
vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
|
|
73
|
-
const promise = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket);
|
|
74
|
-
mockLocalSocket._trigger('connect');
|
|
75
|
-
await promise;
|
|
76
|
-
const testData = Buffer.from('hello world');
|
|
77
|
-
mockLocalSocket._trigger('data', testData);
|
|
78
|
-
expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:data', {
|
|
79
|
-
tunnelId: 'tunnel-1',
|
|
80
|
-
data: testData.toString('base64'),
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
it('should send port-forward:close and remove tunnel when local socket closes', async () => {
|
|
84
|
-
const mockLocalSocket = createMockLocalSocket();
|
|
85
|
-
const mockServerSocket = createMockServerSocket();
|
|
86
|
-
vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
|
|
87
|
-
const promise = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket);
|
|
88
|
-
mockLocalSocket._trigger('connect');
|
|
89
|
-
await promise;
|
|
90
|
-
mockLocalSocket._trigger('close');
|
|
91
|
-
expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:close', {
|
|
92
|
-
tunnelId: 'tunnel-1',
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
it('should send port-forward:error and remove tunnel when local socket errors after connect', async () => {
|
|
96
|
-
const mockLocalSocket = createMockLocalSocket();
|
|
97
|
-
const mockServerSocket = createMockServerSocket();
|
|
98
|
-
vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
|
|
99
|
-
const promise = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket);
|
|
100
|
-
mockLocalSocket._trigger('connect');
|
|
101
|
-
await promise;
|
|
102
|
-
mockLocalSocket._trigger('error', new Error('ECONNRESET'));
|
|
103
|
-
expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:error', {
|
|
104
|
-
tunnelId: 'tunnel-1',
|
|
105
|
-
error: 'ECONNRESET',
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
describe('handleTunnelData', () => {
|
|
110
|
-
it('should write base64 decoded data to the corresponding local socket', async () => {
|
|
111
|
-
const mockLocalSocket = createMockLocalSocket();
|
|
112
|
-
const mockServerSocket = createMockServerSocket();
|
|
113
|
-
vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
|
|
114
|
-
const promise = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket);
|
|
115
|
-
mockLocalSocket._trigger('connect');
|
|
116
|
-
await promise;
|
|
117
|
-
const base64Data = Buffer.from('test payload').toString('base64');
|
|
118
|
-
portForwardManager.handleTunnelData('tunnel-1', base64Data);
|
|
119
|
-
expect(mockLocalSocket.write).toHaveBeenCalledWith(Buffer.from('test payload'));
|
|
120
|
-
});
|
|
121
|
-
it('should log warning and ignore for unknown tunnelId', () => {
|
|
122
|
-
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
123
|
-
portForwardManager.handleTunnelData('unknown-tunnel', 'data');
|
|
124
|
-
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('unknown tunnel'));
|
|
125
|
-
warnSpy.mockRestore();
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
describe('closeTunnel', () => {
|
|
129
|
-
it('should destroy local socket and remove tunnel from map', async () => {
|
|
130
|
-
const mockLocalSocket = createMockLocalSocket();
|
|
131
|
-
const mockServerSocket = createMockServerSocket();
|
|
132
|
-
vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
|
|
133
|
-
const promise = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket);
|
|
134
|
-
mockLocalSocket._trigger('connect');
|
|
135
|
-
await promise;
|
|
136
|
-
portForwardManager.closeTunnel('tunnel-1');
|
|
137
|
-
expect(mockLocalSocket.destroy).toHaveBeenCalled();
|
|
138
|
-
});
|
|
139
|
-
it('should silently ignore unknown tunnelId', () => {
|
|
140
|
-
expect(() => portForwardManager.closeTunnel('unknown-tunnel')).not.toThrow();
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
describe('closeAll', () => {
|
|
144
|
-
it('should close all tunnels and clear the map', async () => {
|
|
145
|
-
const socket1 = createMockLocalSocket();
|
|
146
|
-
const socket2 = createMockLocalSocket();
|
|
147
|
-
const mockServerSocket = createMockServerSocket();
|
|
148
|
-
vi.mocked(net.connect)
|
|
149
|
-
.mockReturnValueOnce(socket1)
|
|
150
|
-
.mockReturnValueOnce(socket2);
|
|
151
|
-
const promise1 = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket);
|
|
152
|
-
socket1._trigger('connect');
|
|
153
|
-
await promise1;
|
|
154
|
-
const promise2 = portForwardManager.createTunnel('tunnel-2', 4000, mockServerSocket);
|
|
155
|
-
socket2._trigger('connect');
|
|
156
|
-
await promise2;
|
|
157
|
-
portForwardManager.closeAll();
|
|
158
|
-
expect(socket1.destroy).toHaveBeenCalled();
|
|
159
|
-
expect(socket2.destroy).toHaveBeenCalled();
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
//# sourceMappingURL=port-forward.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"port-forward.test.js","sourceRoot":"","sources":["../src/port-forward.test.ts"],"names":[],"mappings":"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"}
|
package/src/port-forward.test.ts
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
|
|
3
|
-
vi.mock('node:net', () => ({
|
|
4
|
-
default: {
|
|
5
|
-
connect: vi.fn(),
|
|
6
|
-
},
|
|
7
|
-
}));
|
|
8
|
-
|
|
9
|
-
import net from 'node:net';
|
|
10
|
-
import { PortForwardManager } from './port-forward.js';
|
|
11
|
-
|
|
12
|
-
function createMockLocalSocket() {
|
|
13
|
-
const handlers = new Map<string, Function>();
|
|
14
|
-
return {
|
|
15
|
-
once: vi.fn((event: string, handler: Function) => {
|
|
16
|
-
handlers.set(event, handler);
|
|
17
|
-
}),
|
|
18
|
-
on: vi.fn((event: string, handler: Function) => {
|
|
19
|
-
handlers.set(event, handler);
|
|
20
|
-
}),
|
|
21
|
-
removeListener: vi.fn(),
|
|
22
|
-
destroy: vi.fn(),
|
|
23
|
-
write: vi.fn(),
|
|
24
|
-
emit: vi.fn(),
|
|
25
|
-
_handlers: handlers,
|
|
26
|
-
_trigger(event: string, ...args: unknown[]) {
|
|
27
|
-
const handler = handlers.get(event);
|
|
28
|
-
if (handler) handler(...args);
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function createMockServerSocket() {
|
|
34
|
-
return {
|
|
35
|
-
emit: vi.fn(),
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
describe('PortForwardManager', () => {
|
|
40
|
-
let portForwardManager: PortForwardManager;
|
|
41
|
-
|
|
42
|
-
beforeEach(() => {
|
|
43
|
-
vi.clearAllMocks();
|
|
44
|
-
portForwardManager = new PortForwardManager();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe('createTunnel', () => {
|
|
48
|
-
it('should send port-forward:created(success: true) and store tunnel on connection success', async () => {
|
|
49
|
-
const mockLocalSocket = createMockLocalSocket();
|
|
50
|
-
const mockServerSocket = createMockServerSocket();
|
|
51
|
-
vi.mocked(net.connect).mockReturnValue(mockLocalSocket as never);
|
|
52
|
-
|
|
53
|
-
const promise = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket as never);
|
|
54
|
-
mockLocalSocket._trigger('connect');
|
|
55
|
-
await promise;
|
|
56
|
-
|
|
57
|
-
expect(net.connect).toHaveBeenCalledWith({ host: '127.0.0.1', port: 3000 });
|
|
58
|
-
expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:created', {
|
|
59
|
-
tunnelId: 'tunnel-1',
|
|
60
|
-
success: true,
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should send port-forward:created(success: false, error) and destroy socket on connection failure', async () => {
|
|
65
|
-
const mockLocalSocket = createMockLocalSocket();
|
|
66
|
-
const mockServerSocket = createMockServerSocket();
|
|
67
|
-
vi.mocked(net.connect).mockReturnValue(mockLocalSocket as never);
|
|
68
|
-
|
|
69
|
-
const promise = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket as never);
|
|
70
|
-
mockLocalSocket._trigger('error', new Error('ECONNREFUSED'));
|
|
71
|
-
await promise;
|
|
72
|
-
|
|
73
|
-
expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:created', {
|
|
74
|
-
tunnelId: 'tunnel-1',
|
|
75
|
-
success: false,
|
|
76
|
-
error: 'ECONNREFUSED',
|
|
77
|
-
});
|
|
78
|
-
expect(mockLocalSocket.destroy).toHaveBeenCalled();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should send port-forward:data with base64 encoded data when local socket receives data', async () => {
|
|
82
|
-
const mockLocalSocket = createMockLocalSocket();
|
|
83
|
-
const mockServerSocket = createMockServerSocket();
|
|
84
|
-
vi.mocked(net.connect).mockReturnValue(mockLocalSocket as never);
|
|
85
|
-
|
|
86
|
-
const promise = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket as never);
|
|
87
|
-
mockLocalSocket._trigger('connect');
|
|
88
|
-
await promise;
|
|
89
|
-
|
|
90
|
-
const testData = Buffer.from('hello world');
|
|
91
|
-
mockLocalSocket._trigger('data', testData);
|
|
92
|
-
|
|
93
|
-
expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:data', {
|
|
94
|
-
tunnelId: 'tunnel-1',
|
|
95
|
-
data: testData.toString('base64'),
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should send port-forward:close and remove tunnel when local socket closes', async () => {
|
|
100
|
-
const mockLocalSocket = createMockLocalSocket();
|
|
101
|
-
const mockServerSocket = createMockServerSocket();
|
|
102
|
-
vi.mocked(net.connect).mockReturnValue(mockLocalSocket as never);
|
|
103
|
-
|
|
104
|
-
const promise = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket as never);
|
|
105
|
-
mockLocalSocket._trigger('connect');
|
|
106
|
-
await promise;
|
|
107
|
-
|
|
108
|
-
mockLocalSocket._trigger('close');
|
|
109
|
-
|
|
110
|
-
expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:close', {
|
|
111
|
-
tunnelId: 'tunnel-1',
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should send port-forward:error and remove tunnel when local socket errors after connect', async () => {
|
|
116
|
-
const mockLocalSocket = createMockLocalSocket();
|
|
117
|
-
const mockServerSocket = createMockServerSocket();
|
|
118
|
-
vi.mocked(net.connect).mockReturnValue(mockLocalSocket as never);
|
|
119
|
-
|
|
120
|
-
const promise = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket as never);
|
|
121
|
-
mockLocalSocket._trigger('connect');
|
|
122
|
-
await promise;
|
|
123
|
-
|
|
124
|
-
mockLocalSocket._trigger('error', new Error('ECONNRESET'));
|
|
125
|
-
|
|
126
|
-
expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:error', {
|
|
127
|
-
tunnelId: 'tunnel-1',
|
|
128
|
-
error: 'ECONNRESET',
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
describe('handleTunnelData', () => {
|
|
134
|
-
it('should write base64 decoded data to the corresponding local socket', async () => {
|
|
135
|
-
const mockLocalSocket = createMockLocalSocket();
|
|
136
|
-
const mockServerSocket = createMockServerSocket();
|
|
137
|
-
vi.mocked(net.connect).mockReturnValue(mockLocalSocket as never);
|
|
138
|
-
|
|
139
|
-
const promise = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket as never);
|
|
140
|
-
mockLocalSocket._trigger('connect');
|
|
141
|
-
await promise;
|
|
142
|
-
|
|
143
|
-
const base64Data = Buffer.from('test payload').toString('base64');
|
|
144
|
-
portForwardManager.handleTunnelData('tunnel-1', base64Data);
|
|
145
|
-
|
|
146
|
-
expect(mockLocalSocket.write).toHaveBeenCalledWith(Buffer.from('test payload'));
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('should log warning and ignore for unknown tunnelId', () => {
|
|
150
|
-
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
151
|
-
portForwardManager.handleTunnelData('unknown-tunnel', 'data');
|
|
152
|
-
expect(warnSpy).toHaveBeenCalledWith(
|
|
153
|
-
expect.stringContaining('unknown tunnel'),
|
|
154
|
-
);
|
|
155
|
-
warnSpy.mockRestore();
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
describe('closeTunnel', () => {
|
|
160
|
-
it('should destroy local socket and remove tunnel from map', async () => {
|
|
161
|
-
const mockLocalSocket = createMockLocalSocket();
|
|
162
|
-
const mockServerSocket = createMockServerSocket();
|
|
163
|
-
vi.mocked(net.connect).mockReturnValue(mockLocalSocket as never);
|
|
164
|
-
|
|
165
|
-
const promise = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket as never);
|
|
166
|
-
mockLocalSocket._trigger('connect');
|
|
167
|
-
await promise;
|
|
168
|
-
|
|
169
|
-
portForwardManager.closeTunnel('tunnel-1');
|
|
170
|
-
|
|
171
|
-
expect(mockLocalSocket.destroy).toHaveBeenCalled();
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it('should silently ignore unknown tunnelId', () => {
|
|
175
|
-
expect(() => portForwardManager.closeTunnel('unknown-tunnel')).not.toThrow();
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
describe('closeAll', () => {
|
|
180
|
-
it('should close all tunnels and clear the map', async () => {
|
|
181
|
-
const socket1 = createMockLocalSocket();
|
|
182
|
-
const socket2 = createMockLocalSocket();
|
|
183
|
-
const mockServerSocket = createMockServerSocket();
|
|
184
|
-
vi.mocked(net.connect)
|
|
185
|
-
.mockReturnValueOnce(socket1 as never)
|
|
186
|
-
.mockReturnValueOnce(socket2 as never);
|
|
187
|
-
|
|
188
|
-
const promise1 = portForwardManager.createTunnel('tunnel-1', 3000, mockServerSocket as never);
|
|
189
|
-
socket1._trigger('connect');
|
|
190
|
-
await promise1;
|
|
191
|
-
|
|
192
|
-
const promise2 = portForwardManager.createTunnel('tunnel-2', 4000, mockServerSocket as never);
|
|
193
|
-
socket2._trigger('connect');
|
|
194
|
-
await promise2;
|
|
195
|
-
|
|
196
|
-
portForwardManager.closeAll();
|
|
197
|
-
|
|
198
|
-
expect(socket1.destroy).toHaveBeenCalled();
|
|
199
|
-
expect(socket2.destroy).toHaveBeenCalled();
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
});
|
package/src/port-forward.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import net from 'node:net';
|
|
2
|
-
import type { Socket } from 'socket.io-client';
|
|
3
|
-
|
|
4
|
-
interface Tunnel {
|
|
5
|
-
id: string;
|
|
6
|
-
port: number;
|
|
7
|
-
localSocket: net.Socket;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export class PortForwardManager {
|
|
11
|
-
private tunnels: Map<string, Tunnel> = new Map();
|
|
12
|
-
|
|
13
|
-
async createTunnel(tunnelId: string, port: number, serverSocket: Socket): Promise<void> {
|
|
14
|
-
const localSocket = net.connect({ host: '127.0.0.1', port });
|
|
15
|
-
|
|
16
|
-
return new Promise<void>((resolve) => {
|
|
17
|
-
const onError = (err: Error) => {
|
|
18
|
-
console.error(`Port forward tunnel error: tunnelId=${tunnelId} error=${err.message}`);
|
|
19
|
-
serverSocket.emit('port-forward:created', {
|
|
20
|
-
tunnelId,
|
|
21
|
-
success: false,
|
|
22
|
-
error: err.message,
|
|
23
|
-
});
|
|
24
|
-
localSocket.destroy();
|
|
25
|
-
resolve();
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
localSocket.once('error', onError);
|
|
29
|
-
|
|
30
|
-
localSocket.once('connect', () => {
|
|
31
|
-
localSocket.removeListener('error', onError);
|
|
32
|
-
|
|
33
|
-
this.tunnels.set(tunnelId, { id: tunnelId, port, localSocket });
|
|
34
|
-
console.log(`Port forward tunnel created: tunnelId=${tunnelId} port=${port}`);
|
|
35
|
-
|
|
36
|
-
serverSocket.emit('port-forward:created', { tunnelId, success: true });
|
|
37
|
-
|
|
38
|
-
localSocket.on('data', (data: Buffer) => {
|
|
39
|
-
serverSocket.emit('port-forward:data', {
|
|
40
|
-
tunnelId,
|
|
41
|
-
data: data.toString('base64'),
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
localSocket.on('close', () => {
|
|
46
|
-
console.log(`Port forward local socket closed: tunnelId=${tunnelId}`);
|
|
47
|
-
this.tunnels.delete(tunnelId);
|
|
48
|
-
serverSocket.emit('port-forward:close', { tunnelId });
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
localSocket.on('error', (err: Error) => {
|
|
52
|
-
console.error(`Port forward local socket error: tunnelId=${tunnelId} error=${err.message}`);
|
|
53
|
-
this.tunnels.delete(tunnelId);
|
|
54
|
-
serverSocket.emit('port-forward:error', { tunnelId, error: err.message });
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
resolve();
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
handleTunnelData(tunnelId: string, data: string): void {
|
|
63
|
-
const tunnel = this.tunnels.get(tunnelId);
|
|
64
|
-
if (!tunnel) {
|
|
65
|
-
console.warn(`Port forward data for unknown tunnel: tunnelId=${tunnelId}`);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
tunnel.localSocket.write(Buffer.from(data, 'base64'));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
closeTunnel(tunnelId: string): void {
|
|
72
|
-
const tunnel = this.tunnels.get(tunnelId);
|
|
73
|
-
if (!tunnel) return;
|
|
74
|
-
console.log(`Port forward closing tunnel: tunnelId=${tunnelId}`);
|
|
75
|
-
tunnel.localSocket.destroy();
|
|
76
|
-
this.tunnels.delete(tunnelId);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
closeAll(): void {
|
|
80
|
-
console.log(`Port forward closing all tunnels: count=${this.tunnels.size}`);
|
|
81
|
-
for (const tunnel of this.tunnels.values()) {
|
|
82
|
-
tunnel.localSocket.destroy();
|
|
83
|
-
}
|
|
84
|
-
this.tunnels.clear();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export const portForwardManager = new PortForwardManager();
|