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 CHANGED
@@ -75,3 +75,26 @@ ZAG_CONTROLLER_KEY=zag_k_1_abc123... ZAG_SERVER_ENDPOINT=http://localhost:4001 n
75
75
  ## Related Rules
76
76
 
77
77
  - [Common Rules](../../docs/engineering/common-rules.md)
78
+
79
+ ## Lessons Learned
80
+
81
+ ### 跨服务协议必须有双向集成测试
82
+
83
+ 本次 Proxy timeout bug 的根因是 agent-controller 没有理解 controller-api 发送的 `http-request` JSON 协议,直接把 JSON 字符串当原始 TCP 数据写入了目标服务。测试未覆盖的原因:
84
+
85
+ | 测试层 | 为什么没发现问题 |
86
+ |--------|----------------|
87
+ | `gateway.test.ts`(agent-controller 单元测试) | `handleTunnelData` 只测了纯 TCP 数据(`Buffer.from('test payload')`),从未构造 `http-request` JSON 作为输入 |
88
+ | `gateway-relay.test.ts`(controller-api 单元测试) | Mock 了 Socket.IO 层,构造的 `http-response` 数据格式与 agent-controller 实际发送的格式不一致,但测试双方各自通过 |
89
+ | `gateway-proxy.test.ts`(agent-api 单元测试) | Mock 了整个 Redis Pub/Sub 层,无法检测 controller-api ↔ agent-controller 之间的协议不匹配 |
90
+ | E2E 测试 | Fake controller 的协议实现与真实 agent-controller 不同,掩盖了问题 |
91
+
92
+ **教训**:
93
+ 1. **协议变更时必须同时更新两端的测试** — 当 controller-api 引入 `http-request`/`http-response` 协议时,agent-controller 侧没有添加对应的输入/输出测试用例
94
+ 2. **Mock 隔离测试无法验证跨服务协议匹配** — 即使两端各自单元测试通过,真实交互仍可能失败。关键协议边界需要集成测试
95
+ 3. **伪造实现 vs 真实实现的差异不可忽略** — E2E 的 fake controller 不应与真实 agent-controller 在协议处理上存在差异
96
+
97
+ **改进措施**:
98
+ - 引入新协议或修改现有协议时,先编写跨服务的集成测试(启动真实 agent-controller 或 mock 端精确模拟协议格式)
99
+ - handleTunnelData 的测试必须覆盖所有入站数据类型(纯 TCP 数据 + JSON 协议数据)
100
+ - handleTunnelData 的出站(localSocket.on('data'))测试必须验证响应格式与 controller-api 端期望的格式一致
@@ -7,6 +7,9 @@ export declare class AcpDetector {
7
7
  detect(): Promise<AcpServerInfo[]>;
8
8
  private detectOpenCode;
9
9
  private detectGitHubCopilot;
10
+ private detectClaudeCode;
11
+ private detectGeminiCli;
12
+ private detectCodex;
10
13
  getSpawnerConfig(serverType: string): {
11
14
  command: string;
12
15
  args: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../src/acp/detector.ts"],"names":[],"mappings":"AAKA,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,WAAW;IAChB,MAAM,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;YAQ1B,cAAc;YAUd,mBAAmB;IAUjC,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE;IAUzE,OAAO,CAAC,YAAY;CAIrB;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"}
1
+ {"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../src/acp/detector.ts"],"names":[],"mappings":"AAKA,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,WAAW;IAChB,MAAM,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;YAW1B,cAAc;YAUd,mBAAmB;YAUnB,gBAAgB;YAUhB,eAAe;YAUf,WAAW;IAUzB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE;IAgBzE,OAAO,CAAC,YAAY;CAIrB;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"}
@@ -3,11 +3,14 @@ import { promisify } from 'util';
3
3
  const execAsync = promisify(exec);
4
4
  export class AcpDetector {
5
5
  async detect() {
6
- const [opencode, copilot] = await Promise.all([
6
+ const [opencode, copilot, claudeCode, geminiCli, codex] = await Promise.all([
7
7
  this.detectOpenCode(),
8
8
  this.detectGitHubCopilot(),
9
+ this.detectClaudeCode(),
10
+ this.detectGeminiCli(),
11
+ this.detectCodex(),
9
12
  ]);
10
- return [opencode, copilot];
13
+ return [opencode, copilot, claudeCode, geminiCli, codex];
11
14
  }
12
15
  async detectOpenCode() {
13
16
  try {
@@ -29,10 +32,46 @@ export class AcpDetector {
29
32
  return { type: 'github_copilot', available: false };
30
33
  }
31
34
  }
35
+ async detectClaudeCode() {
36
+ try {
37
+ const { stdout } = await execAsync('claude-agent-acp --version');
38
+ const version = this.parseVersion(stdout);
39
+ return { type: 'claude_code', available: true, version };
40
+ }
41
+ catch {
42
+ return { type: 'claude_code', available: false };
43
+ }
44
+ }
45
+ async detectGeminiCli() {
46
+ try {
47
+ const { stdout } = await execAsync('gemini --version');
48
+ const version = this.parseVersion(stdout);
49
+ return { type: 'gemini_cli', available: true, version };
50
+ }
51
+ catch {
52
+ return { type: 'gemini_cli', available: false };
53
+ }
54
+ }
55
+ async detectCodex() {
56
+ try {
57
+ const { stdout } = await execAsync('codex-acp --version');
58
+ const version = this.parseVersion(stdout);
59
+ return { type: 'codex', available: true, version };
60
+ }
61
+ catch {
62
+ return { type: 'codex', available: false };
63
+ }
64
+ }
32
65
  getSpawnerConfig(serverType) {
33
66
  switch (serverType) {
34
67
  case 'github_copilot':
35
68
  return { command: 'copilot', args: ['--acp', '--stdio'] };
69
+ case 'claude_code':
70
+ return { command: 'claude-agent-acp', args: [] };
71
+ case 'gemini_cli':
72
+ return { command: 'gemini', args: ['--acp'] };
73
+ case 'codex':
74
+ return { command: 'codex-acp', args: [] };
36
75
  case 'opencode':
37
76
  default:
38
77
  return { command: 'opencode', args: ['acp'] };
@@ -1 +1 @@
1
- {"version":3,"file":"detector.js","sourceRoot":"","sources":["../../src/acp/detector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAEjC,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAQlC,MAAM,OAAO,WAAW;IACtB,KAAK,CAAC,MAAM;QACV,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC5C,IAAI,CAAC,cAAc,EAAE;YACrB,IAAI,CAAC,mBAAmB,EAAE;SAC3B,CAAC,CAAC;QACH,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC7B,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,IAAI,CAAC;YACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,aAAa,CAAC,CAAC;YAClD,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAC1C,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QAChD,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,mBAAmB;QAC/B,IAAI,CAAC;YACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,mBAAmB,CAAC,CAAC;YACxD,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAC1C,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QACtD,CAAC;IACH,CAAC;IAED,gBAAgB,CAAC,UAAkB;QACjC,QAAQ,UAAU,EAAE,CAAC;YACnB,KAAK,gBAAgB;gBACnB,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;YAC5D,KAAK,UAAU,CAAC;YAChB;gBACE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,CAAC;IACH,CAAC;IAEO,YAAY,CAAC,MAAc;QACjC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC9C,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"detector.js","sourceRoot":"","sources":["../../src/acp/detector.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,14 @@
1
1
  import type { Socket } from 'socket.io-client';
2
- export declare class PortForwardManager {
2
+ export declare class GatewayManager {
3
3
  private tunnels;
4
+ private reconnectTimers;
4
5
  createTunnel(tunnelId: string, port: number, serverSocket: Socket): Promise<void>;
6
+ private connect;
7
+ private flushResponse;
8
+ private scheduleReconnect;
5
9
  handleTunnelData(tunnelId: string, data: string): void;
6
10
  closeTunnel(tunnelId: string): void;
7
11
  closeAll(): void;
8
12
  }
9
- export declare const portForwardManager: PortForwardManager;
10
- //# sourceMappingURL=port-forward.d.ts.map
13
+ export declare const gatewayManager: GatewayManager;
14
+ //# sourceMappingURL=gateway.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAe/C,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,eAAe,CAAyD;IAE1E,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAKzE,OAAO;IAwDrB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,iBAAiB;IAYzB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IA2CtD,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAoBnC,QAAQ,IAAI,IAAI;CAkBjB;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"}
@@ -0,0 +1,158 @@
1
+ import net from 'node:net';
2
+ const RECONNECT_INTERVAL_MS = 3000;
3
+ const HTTP_RESPONSE_COLLECT_MS = 100;
4
+ export class GatewayManager {
5
+ tunnels = new Map();
6
+ reconnectTimers = new Map();
7
+ async createTunnel(tunnelId, port, serverSocket) {
8
+ this.tunnels.set(tunnelId, { id: tunnelId, port, localSocket: null, serverSocket, responseBuffer: [] });
9
+ await this.connect(tunnelId, true);
10
+ }
11
+ async connect(tunnelId, isInitial) {
12
+ const tunnel = this.tunnels.get(tunnelId);
13
+ if (!tunnel)
14
+ return;
15
+ const localSocket = net.connect({ host: '127.0.0.1', port: tunnel.port });
16
+ localSocket.setKeepAlive(true, 5000);
17
+ return new Promise((resolve) => {
18
+ const onError = (err) => {
19
+ console.error(`Gateway tunnel error: tunnelId=${tunnelId} error=${err.message}`);
20
+ localSocket.destroy();
21
+ this.scheduleReconnect(tunnelId);
22
+ resolve();
23
+ };
24
+ localSocket.once('error', onError);
25
+ localSocket.once('connect', () => {
26
+ localSocket.removeListener('error', onError);
27
+ tunnel.localSocket = localSocket;
28
+ console.log(`Gateway tunnel connected: tunnelId=${tunnelId} port=${tunnel.port}`);
29
+ if (isInitial) {
30
+ tunnel.serverSocket.emit('port-forward:created', { tunnelId, success: true });
31
+ }
32
+ localSocket.on('data', (data) => {
33
+ if (tunnel.pendingRequestId) {
34
+ tunnel.responseBuffer.push(data);
35
+ if (tunnel.responseTimer)
36
+ clearTimeout(tunnel.responseTimer);
37
+ tunnel.responseTimer = setTimeout(() => this.flushResponse(tunnel), HTTP_RESPONSE_COLLECT_MS);
38
+ }
39
+ else {
40
+ tunnel.serverSocket.emit('port-forward:data', {
41
+ tunnelId,
42
+ data: data.toString('base64'),
43
+ });
44
+ }
45
+ });
46
+ localSocket.on('close', () => {
47
+ console.log(`Gateway local socket closed: tunnelId=${tunnelId}`);
48
+ tunnel.localSocket = null;
49
+ this.scheduleReconnect(tunnelId);
50
+ });
51
+ localSocket.on('error', (err) => {
52
+ console.error(`Gateway local socket error: tunnelId=${tunnelId} error=${err.message}`);
53
+ tunnel.serverSocket.emit('port-forward:error', { tunnelId, error: err.message });
54
+ });
55
+ resolve();
56
+ });
57
+ });
58
+ }
59
+ flushResponse(tunnel) {
60
+ if (!tunnel.pendingRequestId)
61
+ return;
62
+ const combined = Buffer.concat(tunnel.responseBuffer);
63
+ tunnel.responseBuffer = [];
64
+ const requestId = tunnel.pendingRequestId;
65
+ tunnel.pendingRequestId = null;
66
+ tunnel.responseTimer = null;
67
+ tunnel.serverSocket.emit('port-forward:data', {
68
+ tunnelId: tunnel.id,
69
+ data: Buffer.from(JSON.stringify({
70
+ type: 'http-response',
71
+ requestId,
72
+ body: combined.toString('base64'),
73
+ })).toString('base64'),
74
+ });
75
+ }
76
+ scheduleReconnect(tunnelId) {
77
+ if (!this.tunnels.has(tunnelId) || this.reconnectTimers.has(tunnelId))
78
+ return;
79
+ console.log(`Gateway scheduling reconnection: tunnelId=${tunnelId}`);
80
+ const timer = setTimeout(() => {
81
+ this.reconnectTimers.delete(tunnelId);
82
+ this.connect(tunnelId, false);
83
+ }, RECONNECT_INTERVAL_MS);
84
+ this.reconnectTimers.set(tunnelId, timer);
85
+ }
86
+ handleTunnelData(tunnelId, data) {
87
+ const tunnel = this.tunnels.get(tunnelId);
88
+ if (!tunnel) {
89
+ console.warn(`Gateway data for unknown tunnel: tunnelId=${tunnelId}`);
90
+ return;
91
+ }
92
+ const buffer = Buffer.from(data, 'base64');
93
+ let parsed;
94
+ try {
95
+ parsed = JSON.parse(buffer.toString('utf8'));
96
+ }
97
+ catch {
98
+ if (tunnel.localSocket) {
99
+ tunnel.localSocket.write(buffer);
100
+ }
101
+ return;
102
+ }
103
+ if (parsed.type === 'http-request') {
104
+ const { method, path, headers, body } = parsed;
105
+ let rawRequest = `${method} ${path} HTTP/1.1\r\n`;
106
+ for (const [key, value] of Object.entries(headers || {})) {
107
+ rawRequest += `${key}: ${value}\r\n`;
108
+ }
109
+ rawRequest += `Host: 127.0.0.1:${tunnel.port}\r\n`;
110
+ rawRequest += 'Connection: close\r\n';
111
+ rawRequest += '\r\n';
112
+ if (body)
113
+ rawRequest += Buffer.from(body, 'base64').toString('utf8');
114
+ tunnel.pendingRequestId = parsed.requestId;
115
+ tunnel.responseBuffer = [];
116
+ if (tunnel.localSocket) {
117
+ tunnel.localSocket.write(rawRequest);
118
+ }
119
+ return;
120
+ }
121
+ if (tunnel.localSocket) {
122
+ tunnel.localSocket.write(buffer);
123
+ }
124
+ }
125
+ closeTunnel(tunnelId) {
126
+ const tunnel = this.tunnels.get(tunnelId);
127
+ if (!tunnel)
128
+ return;
129
+ console.log(`Gateway closing tunnel: tunnelId=${tunnelId}`);
130
+ const timer = this.reconnectTimers.get(tunnelId);
131
+ if (timer) {
132
+ clearTimeout(timer);
133
+ this.reconnectTimers.delete(tunnelId);
134
+ }
135
+ this.tunnels.delete(tunnelId);
136
+ if (tunnel.localSocket) {
137
+ tunnel.localSocket.destroy();
138
+ }
139
+ tunnel.serverSocket.emit('port-forward:close', { tunnelId });
140
+ }
141
+ closeAll() {
142
+ console.log(`Gateway closing all tunnels: count=${this.tunnels.size}`);
143
+ for (const [tunnelId, tunnel] of [...this.tunnels]) {
144
+ const timer = this.reconnectTimers.get(tunnelId);
145
+ if (timer) {
146
+ clearTimeout(timer);
147
+ this.reconnectTimers.delete(tunnelId);
148
+ }
149
+ this.tunnels.delete(tunnelId);
150
+ if (tunnel.localSocket) {
151
+ tunnel.localSocket.destroy();
152
+ }
153
+ tunnel.serverSocket.emit('port-forward:close', { tunnelId: tunnel.id });
154
+ }
155
+ }
156
+ }
157
+ export const gatewayManager = new GatewayManager();
158
+ //# sourceMappingURL=gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gateway.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.test.d.ts","sourceRoot":"","sources":["../src/gateway.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,241 @@
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 { GatewayManager } from './gateway.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
+ setKeepAlive: vi.fn(),
23
+ _handlers: handlers,
24
+ _trigger(event, ...args) {
25
+ const handler = handlers.get(event);
26
+ if (handler)
27
+ handler(...args);
28
+ },
29
+ };
30
+ }
31
+ function createMockServerSocket() {
32
+ return {
33
+ emit: vi.fn(),
34
+ };
35
+ }
36
+ describe('GatewayManager', () => {
37
+ let gatewayManager;
38
+ beforeEach(() => {
39
+ vi.clearAllMocks();
40
+ gatewayManager = new GatewayManager();
41
+ });
42
+ describe('createTunnel', () => {
43
+ it('should send port-forward:created(success: true) and store tunnel on connection success', async () => {
44
+ const mockLocalSocket = createMockLocalSocket();
45
+ const mockServerSocket = createMockServerSocket();
46
+ vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
47
+ const promise = gatewayManager.createTunnel('tunnel-1', 3000, mockServerSocket);
48
+ mockLocalSocket._trigger('connect');
49
+ await promise;
50
+ expect(net.connect).toHaveBeenCalledWith({ host: '127.0.0.1', port: 3000 });
51
+ expect(mockLocalSocket.setKeepAlive).toHaveBeenCalledWith(true, 5000);
52
+ expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:created', {
53
+ tunnelId: 'tunnel-1',
54
+ success: true,
55
+ });
56
+ });
57
+ it('should destroy socket and schedule reconnect on connection failure (not emit failure to server)', async () => {
58
+ const mockLocalSocket = createMockLocalSocket();
59
+ const mockServerSocket = createMockServerSocket();
60
+ vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
61
+ const promise = gatewayManager.createTunnel('tunnel-1', 3000, mockServerSocket);
62
+ mockLocalSocket._trigger('error', new Error('ECONNREFUSED'));
63
+ await promise;
64
+ expect(mockServerSocket.emit).not.toHaveBeenCalled();
65
+ expect(mockLocalSocket.destroy).toHaveBeenCalled();
66
+ });
67
+ it('should send port-forward:data with base64 encoded data when local socket receives data', async () => {
68
+ const mockLocalSocket = createMockLocalSocket();
69
+ const mockServerSocket = createMockServerSocket();
70
+ vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
71
+ const promise = gatewayManager.createTunnel('tunnel-1', 3000, mockServerSocket);
72
+ mockLocalSocket._trigger('connect');
73
+ await promise;
74
+ const testData = Buffer.from('hello world');
75
+ mockLocalSocket._trigger('data', testData);
76
+ expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:data', {
77
+ tunnelId: 'tunnel-1',
78
+ data: testData.toString('base64'),
79
+ });
80
+ });
81
+ it('should schedule reconnect (not emit close) when local socket closes', async () => {
82
+ const mockLocalSocket = createMockLocalSocket();
83
+ const mockServerSocket = createMockServerSocket();
84
+ vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
85
+ const promise = gatewayManager.createTunnel('tunnel-1', 3000, mockServerSocket);
86
+ mockLocalSocket._trigger('connect');
87
+ await promise;
88
+ mockLocalSocket._trigger('close');
89
+ expect(mockServerSocket.emit).not.toHaveBeenCalledWith('port-forward:close', expect.anything());
90
+ });
91
+ it('should send port-forward:error on local socket error after connect', async () => {
92
+ const mockLocalSocket = createMockLocalSocket();
93
+ const mockServerSocket = createMockServerSocket();
94
+ vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
95
+ const promise = gatewayManager.createTunnel('tunnel-1', 3000, mockServerSocket);
96
+ mockLocalSocket._trigger('connect');
97
+ await promise;
98
+ mockLocalSocket._trigger('error', new Error('ECONNRESET'));
99
+ expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:error', {
100
+ tunnelId: 'tunnel-1',
101
+ error: 'ECONNRESET',
102
+ });
103
+ });
104
+ });
105
+ describe('handleTunnelData', () => {
106
+ it('should write base64 decoded data to the corresponding local socket', async () => {
107
+ const mockLocalSocket = createMockLocalSocket();
108
+ const mockServerSocket = createMockServerSocket();
109
+ vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
110
+ const promise = gatewayManager.createTunnel('tunnel-1', 3000, mockServerSocket);
111
+ mockLocalSocket._trigger('connect');
112
+ await promise;
113
+ const base64Data = Buffer.from('test payload').toString('base64');
114
+ gatewayManager.handleTunnelData('tunnel-1', base64Data);
115
+ expect(mockLocalSocket.write).toHaveBeenCalledWith(Buffer.from('test payload'));
116
+ });
117
+ it('should parse http-request JSON and write raw HTTP request to local socket', async () => {
118
+ const mockLocalSocket = createMockLocalSocket();
119
+ const mockServerSocket = createMockServerSocket();
120
+ vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
121
+ const promise = gatewayManager.createTunnel('tunnel-1', 3000, mockServerSocket);
122
+ mockLocalSocket._trigger('connect');
123
+ await promise;
124
+ const httpRequest = {
125
+ type: 'http-request',
126
+ requestId: 'req-1',
127
+ method: 'POST',
128
+ path: '/api/test',
129
+ headers: { 'content-type': 'application/json', 'x-custom': 'val' },
130
+ body: Buffer.from('{"key":"value"}').toString('base64'),
131
+ };
132
+ const base64Data = Buffer.from(JSON.stringify(httpRequest)).toString('base64');
133
+ gatewayManager.handleTunnelData('tunnel-1', base64Data);
134
+ const expected = 'POST /api/test HTTP/1.1\r\n' +
135
+ 'content-type: application/json\r\n' +
136
+ 'x-custom: val\r\n' +
137
+ 'Host: 127.0.0.1:3000\r\n' +
138
+ 'Connection: close\r\n' +
139
+ '\r\n' +
140
+ '{"key":"value"}';
141
+ expect(mockLocalSocket.write).toHaveBeenCalledWith(expected);
142
+ });
143
+ it('should send raw base64 TCP response when no pending http-request', async () => {
144
+ const mockLocalSocket = createMockLocalSocket();
145
+ const mockServerSocket = createMockServerSocket();
146
+ vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
147
+ const promise = gatewayManager.createTunnel('tunnel-1', 3000, mockServerSocket);
148
+ mockLocalSocket._trigger('connect');
149
+ await promise;
150
+ const testData = Buffer.from('HTTP/1.1 200 OK\r\n\r\nok');
151
+ mockLocalSocket._trigger('data', testData);
152
+ expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:data', {
153
+ tunnelId: 'tunnel-1',
154
+ data: testData.toString('base64'),
155
+ });
156
+ });
157
+ it('should wrap TCP response in http-response JSON when pendingRequestId is set', async () => {
158
+ vi.useFakeTimers();
159
+ const mockLocalSocket = createMockLocalSocket();
160
+ const mockServerSocket = createMockServerSocket();
161
+ vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
162
+ const promise = gatewayManager.createTunnel('tunnel-1', 3000, mockServerSocket);
163
+ mockLocalSocket._trigger('connect');
164
+ await promise;
165
+ const httpRequest = {
166
+ type: 'http-request',
167
+ requestId: 'req-42',
168
+ method: 'GET',
169
+ path: '/',
170
+ headers: {},
171
+ };
172
+ gatewayManager.handleTunnelData('tunnel-1', Buffer.from(JSON.stringify(httpRequest)).toString('base64'));
173
+ mockLocalSocket.write.mockClear();
174
+ mockServerSocket.emit.mockClear();
175
+ const tcpResponse = Buffer.from('HTTP/1.1 200 OK\r\ncontent-length: 2\r\n\r\nok');
176
+ mockLocalSocket._trigger('data', tcpResponse);
177
+ expect(mockServerSocket.emit).not.toHaveBeenCalled();
178
+ vi.advanceTimersByTime(150);
179
+ expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:data', expect.objectContaining({
180
+ tunnelId: 'tunnel-1',
181
+ }));
182
+ const emitArg = mockServerSocket.emit.mock.calls.find((c) => c[0] === 'port-forward:data')?.[1];
183
+ expect(emitArg).toBeDefined();
184
+ const decoded = JSON.parse(Buffer.from(emitArg.data, 'base64').toString('utf8'));
185
+ expect(decoded.type).toBe('http-response');
186
+ expect(decoded.requestId).toBe('req-42');
187
+ expect(decoded.body).toBe(tcpResponse.toString('base64'));
188
+ vi.useRealTimers();
189
+ });
190
+ it('should log warning and ignore for unknown tunnelId', () => {
191
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
192
+ gatewayManager.handleTunnelData('unknown-tunnel', 'data');
193
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('unknown tunnel'));
194
+ warnSpy.mockRestore();
195
+ });
196
+ });
197
+ describe('closeTunnel', () => {
198
+ it('should destroy local socket, emit close, and remove tunnel from map', async () => {
199
+ const mockLocalSocket = createMockLocalSocket();
200
+ const mockServerSocket = createMockServerSocket();
201
+ vi.mocked(net.connect).mockReturnValue(mockLocalSocket);
202
+ const promise = gatewayManager.createTunnel('tunnel-1', 3000, mockServerSocket);
203
+ mockLocalSocket._trigger('connect');
204
+ await promise;
205
+ gatewayManager.closeTunnel('tunnel-1');
206
+ expect(mockLocalSocket.destroy).toHaveBeenCalled();
207
+ expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:close', {
208
+ tunnelId: 'tunnel-1',
209
+ });
210
+ });
211
+ it('should silently ignore unknown tunnelId', () => {
212
+ expect(() => gatewayManager.closeTunnel('unknown-tunnel')).not.toThrow();
213
+ });
214
+ });
215
+ describe('closeAll', () => {
216
+ it('should close all tunnels, emit close events, and clear the map', async () => {
217
+ const socket1 = createMockLocalSocket();
218
+ const socket2 = createMockLocalSocket();
219
+ const mockServerSocket = createMockServerSocket();
220
+ vi.mocked(net.connect)
221
+ .mockReturnValueOnce(socket1)
222
+ .mockReturnValueOnce(socket2);
223
+ const promise1 = gatewayManager.createTunnel('tunnel-1', 3000, mockServerSocket);
224
+ socket1._trigger('connect');
225
+ await promise1;
226
+ const promise2 = gatewayManager.createTunnel('tunnel-2', 4000, mockServerSocket);
227
+ socket2._trigger('connect');
228
+ await promise2;
229
+ gatewayManager.closeAll();
230
+ expect(socket1.destroy).toHaveBeenCalled();
231
+ expect(socket2.destroy).toHaveBeenCalled();
232
+ expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:close', {
233
+ tunnelId: 'tunnel-1',
234
+ });
235
+ expect(mockServerSocket.emit).toHaveBeenCalledWith('port-forward:close', {
236
+ tunnelId: 'tunnel-2',
237
+ });
238
+ });
239
+ });
240
+ });
241
+ //# sourceMappingURL=gateway.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.test.js","sourceRoot":"","sources":["../src/gateway.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"socketio-client.d.ts","sourceRoot":"","sources":["../src/socketio-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA+I3E,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAChC,MAAM,CA8dR;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE/C"}
1
+ {"version":3,"file":"socketio-client.d.ts","sourceRoot":"","sources":["../src/socketio-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAiJ3E,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAChC,MAAM,CAkeR;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE/C"}