@cluesmith/codev 2.0.0-rc.60 → 2.0.0-rc.63
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/dashboard/dist/assets/index-C7FtNK6Y.css +32 -0
- package/dashboard/dist/assets/index-D6VqWAaI.js +131 -0
- package/dashboard/dist/assets/index-D6VqWAaI.js.map +1 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/agent-farm/cli.d.ts.map +1 -1
- package/dist/agent-farm/cli.js +75 -50
- package/dist/agent-farm/cli.js.map +1 -1
- package/dist/agent-farm/commands/architect.d.ts.map +1 -1
- package/dist/agent-farm/commands/architect.js +38 -48
- package/dist/agent-farm/commands/architect.js.map +1 -1
- package/dist/agent-farm/commands/attach.d.ts.map +1 -1
- package/dist/agent-farm/commands/attach.js +14 -35
- package/dist/agent-farm/commands/attach.js.map +1 -1
- package/dist/agent-farm/commands/cleanup.d.ts.map +1 -1
- package/dist/agent-farm/commands/cleanup.js +17 -18
- package/dist/agent-farm/commands/cleanup.js.map +1 -1
- package/dist/agent-farm/commands/consult.d.ts +3 -4
- package/dist/agent-farm/commands/consult.d.ts.map +1 -1
- package/dist/agent-farm/commands/consult.js +27 -37
- package/dist/agent-farm/commands/consult.js.map +1 -1
- package/dist/agent-farm/commands/open.d.ts.map +1 -1
- package/dist/agent-farm/commands/open.js +19 -36
- package/dist/agent-farm/commands/open.js.map +1 -1
- package/dist/agent-farm/commands/shell.d.ts +3 -3
- package/dist/agent-farm/commands/shell.d.ts.map +1 -1
- package/dist/agent-farm/commands/shell.js +33 -78
- package/dist/agent-farm/commands/shell.js.map +1 -1
- package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
- package/dist/agent-farm/commands/spawn.js +113 -90
- package/dist/agent-farm/commands/spawn.js.map +1 -1
- package/dist/agent-farm/commands/start.d.ts +7 -20
- package/dist/agent-farm/commands/start.d.ts.map +1 -1
- package/dist/agent-farm/commands/start.js +7 -243
- package/dist/agent-farm/commands/start.js.map +1 -1
- package/dist/agent-farm/commands/status.d.ts.map +1 -1
- package/dist/agent-farm/commands/status.js +22 -29
- package/dist/agent-farm/commands/status.js.map +1 -1
- package/dist/agent-farm/commands/stop.d.ts.map +1 -1
- package/dist/agent-farm/commands/stop.js +43 -172
- package/dist/agent-farm/commands/stop.js.map +1 -1
- package/dist/agent-farm/commands/tower-cloud.d.ts +47 -0
- package/dist/agent-farm/commands/tower-cloud.d.ts.map +1 -0
- package/dist/agent-farm/commands/tower-cloud.js +316 -0
- package/dist/agent-farm/commands/tower-cloud.js.map +1 -0
- package/dist/agent-farm/db/index.d.ts +6 -2
- package/dist/agent-farm/db/index.d.ts.map +1 -1
- package/dist/agent-farm/db/index.js +56 -31
- package/dist/agent-farm/db/index.js.map +1 -1
- package/dist/agent-farm/db/migrate.d.ts +0 -4
- package/dist/agent-farm/db/migrate.d.ts.map +1 -1
- package/dist/agent-farm/db/migrate.js +0 -46
- package/dist/agent-farm/db/migrate.js.map +1 -1
- package/dist/agent-farm/db/schema.d.ts +3 -3
- package/dist/agent-farm/db/schema.d.ts.map +1 -1
- package/dist/agent-farm/db/schema.js +3 -17
- package/dist/agent-farm/db/schema.js.map +1 -1
- package/dist/agent-farm/db/types.d.ts +0 -10
- package/dist/agent-farm/db/types.d.ts.map +1 -1
- package/dist/agent-farm/db/types.js +0 -8
- package/dist/agent-farm/db/types.js.map +1 -1
- package/dist/agent-farm/hq-connector.d.ts +1 -1
- package/dist/agent-farm/hq-connector.js +1 -1
- package/dist/agent-farm/lib/cloud-config.d.ts +46 -0
- package/dist/agent-farm/lib/cloud-config.d.ts.map +1 -0
- package/dist/agent-farm/lib/cloud-config.js +106 -0
- package/dist/agent-farm/lib/cloud-config.js.map +1 -0
- package/dist/agent-farm/lib/tower-client.d.ts +7 -5
- package/dist/agent-farm/lib/tower-client.d.ts.map +1 -1
- package/dist/agent-farm/lib/tower-client.js.map +1 -1
- package/dist/agent-farm/lib/tunnel-client.d.ts +117 -0
- package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -0
- package/dist/agent-farm/lib/tunnel-client.js +502 -0
- package/dist/agent-farm/lib/tunnel-client.js.map +1 -0
- package/dist/agent-farm/servers/tower-server.js +865 -441
- package/dist/agent-farm/servers/tower-server.js.map +1 -1
- package/dist/agent-farm/state.d.ts +2 -2
- package/dist/agent-farm/state.d.ts.map +1 -1
- package/dist/agent-farm/state.js +6 -16
- package/dist/agent-farm/state.js.map +1 -1
- package/dist/agent-farm/types.d.ts +1 -18
- package/dist/agent-farm/types.d.ts.map +1 -1
- package/dist/agent-farm/utils/config.d.ts +0 -5
- package/dist/agent-farm/utils/config.d.ts.map +1 -1
- package/dist/agent-farm/utils/config.js +0 -31
- package/dist/agent-farm/utils/config.js.map +1 -1
- package/dist/agent-farm/utils/file-tabs.d.ts +27 -0
- package/dist/agent-farm/utils/file-tabs.d.ts.map +1 -0
- package/dist/agent-farm/utils/file-tabs.js +46 -0
- package/dist/agent-farm/utils/file-tabs.js.map +1 -0
- package/dist/agent-farm/utils/gate-status.d.ts +16 -0
- package/dist/agent-farm/utils/gate-status.d.ts.map +1 -0
- package/dist/agent-farm/utils/gate-status.js +79 -0
- package/dist/agent-farm/utils/gate-status.js.map +1 -0
- package/dist/agent-farm/utils/gate-watcher.d.ts +38 -0
- package/dist/agent-farm/utils/gate-watcher.d.ts.map +1 -0
- package/dist/agent-farm/utils/gate-watcher.js +122 -0
- package/dist/agent-farm/utils/gate-watcher.js.map +1 -0
- package/dist/agent-farm/utils/index.d.ts +0 -1
- package/dist/agent-farm/utils/index.d.ts.map +1 -1
- package/dist/agent-farm/utils/index.js +0 -1
- package/dist/agent-farm/utils/index.js.map +1 -1
- package/dist/agent-farm/utils/notifications.js +1 -1
- package/dist/agent-farm/utils/notifications.js.map +1 -1
- package/dist/agent-farm/utils/server-utils.d.ts +1 -1
- package/dist/agent-farm/utils/server-utils.js +1 -1
- package/dist/agent-farm/utils/session.d.ts +10 -0
- package/dist/agent-farm/utils/session.d.ts.map +1 -0
- package/dist/agent-farm/utils/session.js +12 -0
- package/dist/agent-farm/utils/session.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt.js +1 -1
- package/dist/commands/adopt.js.map +1 -1
- package/dist/commands/consult/index.d.ts +1 -0
- package/dist/commands/consult/index.d.ts.map +1 -1
- package/dist/commands/consult/index.js +42 -15
- package/dist/commands/consult/index.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/porch/index.d.ts.map +1 -1
- package/dist/commands/porch/index.js +35 -12
- package/dist/commands/porch/index.js.map +1 -1
- package/dist/commands/porch/next.js +15 -5
- package/dist/commands/porch/next.js.map +1 -1
- package/dist/commands/porch/verdict.d.ts +8 -0
- package/dist/commands/porch/verdict.d.ts.map +1 -1
- package/dist/commands/porch/verdict.js +13 -0
- package/dist/commands/porch/verdict.js.map +1 -1
- package/dist/terminal/pty-session.d.ts +2 -0
- package/dist/terminal/pty-session.d.ts.map +1 -1
- package/dist/terminal/pty-session.js +4 -0
- package/dist/terminal/pty-session.js.map +1 -1
- package/package.json +1 -1
- package/skeleton/.claude/skills/af/SKILL.md +15 -0
- package/skeleton/consult-types/impl-review.md +9 -0
- package/skeleton/protocols/spir/prompts/review.md +15 -16
- package/skeleton/protocols/spir/protocol.json +4 -0
- package/skeleton/protocols/spir/templates/review.md +81 -199
- package/skeleton/resources/commands/agent-farm.md +38 -2
- package/templates/tower.html +7 -150
- package/dashboard/dist/assets/index-CXloFYpB.css +0 -32
- package/dashboard/dist/assets/index-Ca2fjOJf.js +0 -131
- package/dashboard/dist/assets/index-Ca2fjOJf.js.map +0 -1
- package/dist/agent-farm/utils/orphan-handler.d.ts +0 -27
- package/dist/agent-farm/utils/orphan-handler.d.ts.map +0 -1
- package/dist/agent-farm/utils/orphan-handler.js +0 -149
- package/dist/agent-farm/utils/orphan-handler.js.map +0 -1
- package/dist/agent-farm/utils/port-registry.d.ts +0 -57
- package/dist/agent-farm/utils/port-registry.d.ts.map +0 -1
- package/dist/agent-farm/utils/port-registry.js +0 -166
- package/dist/agent-farm/utils/port-registry.js.map +0 -1
- package/dist/agent-farm/utils/terminal-ports.d.ts +0 -18
- package/dist/agent-farm/utils/terminal-ports.d.ts.map +0 -1
- package/dist/agent-farm/utils/terminal-ports.js +0 -35
- package/dist/agent-farm/utils/terminal-ports.js.map +0 -1
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP/2 Role-Reversal Tunnel Client (Spec 0097 Phase 3, TICK-001)
|
|
3
|
+
*
|
|
4
|
+
* Opens a WebSocket connection to codevos.ai/tunnel, authenticates
|
|
5
|
+
* with JSON messages, then runs an HTTP/2 *server* over the WebSocket
|
|
6
|
+
* stream. codevos.ai acts as the HTTP/2 *client*, sending requests
|
|
7
|
+
* through the tunnel. The tower proxies those requests to localhost.
|
|
8
|
+
*
|
|
9
|
+
* TICK-001: Transport changed from raw TCP/TLS to WebSocket.
|
|
10
|
+
* The H2 role-reversal is transport-agnostic — it works over any duplex stream.
|
|
11
|
+
*/
|
|
12
|
+
import http2 from 'node:http2';
|
|
13
|
+
import http from 'node:http';
|
|
14
|
+
import https from 'node:https';
|
|
15
|
+
import { URL } from 'node:url';
|
|
16
|
+
import WebSocket, { createWebSocketStream } from 'ws';
|
|
17
|
+
/** Headers that must be stripped when proxying between HTTP/2 and HTTP/1.1 */
|
|
18
|
+
const HOP_BY_HOP_HEADERS = new Set([
|
|
19
|
+
'connection',
|
|
20
|
+
'keep-alive',
|
|
21
|
+
'proxy-authenticate',
|
|
22
|
+
'proxy-authorization',
|
|
23
|
+
'te',
|
|
24
|
+
'trailers',
|
|
25
|
+
'transfer-encoding',
|
|
26
|
+
'upgrade',
|
|
27
|
+
]);
|
|
28
|
+
/** Paths that are local-only management endpoints — block from tunnel */
|
|
29
|
+
const BLOCKED_PATH_PREFIX = '/api/tunnel/';
|
|
30
|
+
/**
|
|
31
|
+
* Calculate reconnection backoff with exponential increase and jitter.
|
|
32
|
+
* Exported for unit testing.
|
|
33
|
+
*
|
|
34
|
+
* Formula: min(1000 * 2^attempt + random(0, 1000), 60000)
|
|
35
|
+
* After 10 consecutive failures: 300000ms (5 min)
|
|
36
|
+
*/
|
|
37
|
+
export function calculateBackoff(attempt, randomFn = Math.random) {
|
|
38
|
+
if (attempt >= 10)
|
|
39
|
+
return 300_000;
|
|
40
|
+
const base = 1000 * Math.pow(2, attempt);
|
|
41
|
+
const jitter = Math.floor(randomFn() * 1000);
|
|
42
|
+
return Math.min(base + jitter, 60_000);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Check if a request path should be blocked from tunnel proxying.
|
|
46
|
+
* Normalizes percent-encoding and collapses dot segments before checking,
|
|
47
|
+
* preventing bypass via encoded slashes (%2F), double dots, etc.
|
|
48
|
+
* Exported for unit testing.
|
|
49
|
+
*/
|
|
50
|
+
export function isBlockedPath(path) {
|
|
51
|
+
try {
|
|
52
|
+
// Decode percent-encoding, then resolve dot segments via URL normalization
|
|
53
|
+
const decoded = decodeURIComponent(path);
|
|
54
|
+
// Collapse duplicate slashes and resolve . / .. segments
|
|
55
|
+
const normalized = new URL(decoded, 'http://localhost').pathname;
|
|
56
|
+
return normalized.startsWith(BLOCKED_PATH_PREFIX);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// If decoding fails, check the raw path as a fallback (fail closed)
|
|
60
|
+
return path.startsWith(BLOCKED_PATH_PREFIX);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Filter hop-by-hop headers from a headers object.
|
|
65
|
+
* Returns a new object with only end-to-end headers.
|
|
66
|
+
* Exported for unit testing.
|
|
67
|
+
*/
|
|
68
|
+
export function filterHopByHopHeaders(headers) {
|
|
69
|
+
const result = {};
|
|
70
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
71
|
+
if (value !== undefined && !HOP_BY_HOP_HEADERS.has(key.toLowerCase())) {
|
|
72
|
+
result[key] = value;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Build the WebSocket tunnel URL from the server URL.
|
|
79
|
+
* https:// → wss://, http:// → ws://
|
|
80
|
+
*/
|
|
81
|
+
function buildTunnelWsUrl(serverUrl) {
|
|
82
|
+
const parsed = new URL(serverUrl);
|
|
83
|
+
const wsProtocol = parsed.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
84
|
+
return `${wsProtocol}//${parsed.host}/tunnel`;
|
|
85
|
+
}
|
|
86
|
+
export class TunnelClient {
|
|
87
|
+
options;
|
|
88
|
+
state = 'disconnected';
|
|
89
|
+
connectedAt = null;
|
|
90
|
+
stateListeners = [];
|
|
91
|
+
ws = null;
|
|
92
|
+
wsStream = null;
|
|
93
|
+
h2Server = null;
|
|
94
|
+
h2Session = null;
|
|
95
|
+
reconnectTimer = null;
|
|
96
|
+
consecutiveFailures = 0;
|
|
97
|
+
rateLimitCount = 0;
|
|
98
|
+
destroyed = false;
|
|
99
|
+
constructor(options) {
|
|
100
|
+
this.options = options;
|
|
101
|
+
}
|
|
102
|
+
getState() {
|
|
103
|
+
return this.state;
|
|
104
|
+
}
|
|
105
|
+
getUptime() {
|
|
106
|
+
if (this.state !== 'connected' || this.connectedAt === null)
|
|
107
|
+
return null;
|
|
108
|
+
return Date.now() - this.connectedAt;
|
|
109
|
+
}
|
|
110
|
+
onStateChange(callback) {
|
|
111
|
+
this.stateListeners.push(callback);
|
|
112
|
+
}
|
|
113
|
+
setState(newState) {
|
|
114
|
+
if (this.state === newState)
|
|
115
|
+
return;
|
|
116
|
+
const prev = this.state;
|
|
117
|
+
this.state = newState;
|
|
118
|
+
if (newState === 'connected') {
|
|
119
|
+
this.connectedAt = Date.now();
|
|
120
|
+
this.consecutiveFailures = 0;
|
|
121
|
+
this.rateLimitCount = 0;
|
|
122
|
+
// Push cached metadata on connect
|
|
123
|
+
if (this._pendingMetadata) {
|
|
124
|
+
this.pushMetadataViaHttp(this._pendingMetadata);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else if (newState === 'disconnected' || newState === 'auth_failed') {
|
|
128
|
+
this.connectedAt = null;
|
|
129
|
+
}
|
|
130
|
+
for (const listener of this.stateListeners) {
|
|
131
|
+
try {
|
|
132
|
+
listener(newState, prev);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Ignore listener errors
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Initiate tunnel connection. Non-blocking — connection happens asynchronously.
|
|
141
|
+
*/
|
|
142
|
+
connect() {
|
|
143
|
+
if (this.state === 'connecting' || this.state === 'connected')
|
|
144
|
+
return;
|
|
145
|
+
this.destroyed = false;
|
|
146
|
+
this.clearReconnectTimer();
|
|
147
|
+
this.doConnect();
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Gracefully disconnect the tunnel.
|
|
151
|
+
*/
|
|
152
|
+
disconnect() {
|
|
153
|
+
this.destroyed = true;
|
|
154
|
+
this.clearReconnectTimer();
|
|
155
|
+
this.cleanup();
|
|
156
|
+
this.setState('disconnected');
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Reset the circuit breaker (e.g. after config change).
|
|
160
|
+
* Allows reconnection after auth_failed state.
|
|
161
|
+
*/
|
|
162
|
+
resetCircuitBreaker() {
|
|
163
|
+
if (this.state === 'auth_failed') {
|
|
164
|
+
this.destroyed = false;
|
|
165
|
+
this.consecutiveFailures = 0;
|
|
166
|
+
this.rateLimitCount = 0;
|
|
167
|
+
this.setState('disconnected');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Send tower metadata to codevos.ai.
|
|
172
|
+
*
|
|
173
|
+
* Uses a dual mechanism:
|
|
174
|
+
* 1. Caches metadata for `GET /__tower/metadata` (served when codevos.ai H2 client polls)
|
|
175
|
+
* 2. When connected, proactively POSTs to `serverUrl/api/tower/metadata` via HTTPS
|
|
176
|
+
*
|
|
177
|
+
* Call before `connect()` to set initial metadata, or after to update it.
|
|
178
|
+
*/
|
|
179
|
+
sendMetadata(metadata) {
|
|
180
|
+
this._pendingMetadata = metadata;
|
|
181
|
+
// Proactively push via HTTPS when connected
|
|
182
|
+
if (this.state === 'connected') {
|
|
183
|
+
this.pushMetadataViaHttp(metadata);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/** Stored metadata for serving via GET /__tower/metadata */
|
|
187
|
+
_pendingMetadata = null;
|
|
188
|
+
/**
|
|
189
|
+
* Push metadata to codevos.ai via outbound HTTPS POST.
|
|
190
|
+
* Best-effort — failures are silently ignored since codevos.ai
|
|
191
|
+
* can also poll via the H2 tunnel's GET /__tower/metadata handler.
|
|
192
|
+
*/
|
|
193
|
+
pushMetadataViaHttp(metadata) {
|
|
194
|
+
try {
|
|
195
|
+
const url = new URL('/api/tower/metadata', this.options.serverUrl);
|
|
196
|
+
const body = JSON.stringify(metadata);
|
|
197
|
+
const isSecure = url.protocol === 'https:';
|
|
198
|
+
const transport = isSecure ? https : http;
|
|
199
|
+
const req = transport.request(url, {
|
|
200
|
+
method: 'POST',
|
|
201
|
+
headers: {
|
|
202
|
+
'Content-Type': 'application/json',
|
|
203
|
+
'Authorization': `Bearer ${this.options.apiKey}`,
|
|
204
|
+
'Content-Length': Buffer.byteLength(body),
|
|
205
|
+
},
|
|
206
|
+
}, (res) => {
|
|
207
|
+
res.resume(); // Drain response
|
|
208
|
+
});
|
|
209
|
+
req.on('error', () => {
|
|
210
|
+
// Best-effort — silently ignore network errors
|
|
211
|
+
});
|
|
212
|
+
req.end(body);
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Ignore URL construction or other errors
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
clearReconnectTimer() {
|
|
219
|
+
if (this.reconnectTimer) {
|
|
220
|
+
clearTimeout(this.reconnectTimer);
|
|
221
|
+
this.reconnectTimer = null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
scheduleReconnect() {
|
|
225
|
+
if (this.destroyed || this.state === 'auth_failed')
|
|
226
|
+
return;
|
|
227
|
+
const delay = calculateBackoff(this.consecutiveFailures);
|
|
228
|
+
this.reconnectTimer = setTimeout(() => {
|
|
229
|
+
this.reconnectTimer = null;
|
|
230
|
+
if (!this.destroyed && this.state !== 'auth_failed') {
|
|
231
|
+
this.doConnect();
|
|
232
|
+
}
|
|
233
|
+
}, delay);
|
|
234
|
+
}
|
|
235
|
+
cleanup() {
|
|
236
|
+
if (this.h2Session && !this.h2Session.destroyed) {
|
|
237
|
+
this.h2Session.destroy();
|
|
238
|
+
}
|
|
239
|
+
this.h2Session = null;
|
|
240
|
+
if (this.h2Server) {
|
|
241
|
+
this.h2Server.close();
|
|
242
|
+
}
|
|
243
|
+
this.h2Server = null;
|
|
244
|
+
if (this.wsStream) {
|
|
245
|
+
this.wsStream.destroy();
|
|
246
|
+
}
|
|
247
|
+
this.wsStream = null;
|
|
248
|
+
if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {
|
|
249
|
+
this.ws.close();
|
|
250
|
+
}
|
|
251
|
+
this.ws = null;
|
|
252
|
+
}
|
|
253
|
+
doConnect() {
|
|
254
|
+
this.setState('connecting');
|
|
255
|
+
const wsUrl = buildTunnelWsUrl(this.options.serverUrl);
|
|
256
|
+
const ws = new WebSocket(wsUrl);
|
|
257
|
+
this.ws = ws;
|
|
258
|
+
ws.on('open', () => {
|
|
259
|
+
this.onWsOpen(ws);
|
|
260
|
+
});
|
|
261
|
+
ws.on('error', (err) => {
|
|
262
|
+
// Ignore events from stale WebSockets (e.g. after disconnect + reconnect)
|
|
263
|
+
if (ws !== this.ws)
|
|
264
|
+
return;
|
|
265
|
+
this.handleConnectionError(err);
|
|
266
|
+
});
|
|
267
|
+
ws.on('close', () => {
|
|
268
|
+
// Ignore events from stale WebSockets (e.g. after disconnect + reconnect)
|
|
269
|
+
if (ws !== this.ws)
|
|
270
|
+
return;
|
|
271
|
+
if (this.state === 'connected' || this.state === 'connecting') {
|
|
272
|
+
this.cleanup();
|
|
273
|
+
this.setState('disconnected');
|
|
274
|
+
this.consecutiveFailures++;
|
|
275
|
+
this.scheduleReconnect();
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
onWsOpen(ws) {
|
|
280
|
+
// Send JSON auth message (TICK-001 protocol)
|
|
281
|
+
ws.send(JSON.stringify({ type: 'auth', apiKey: this.options.apiKey }));
|
|
282
|
+
// Wait for auth response
|
|
283
|
+
const onMessage = (data) => {
|
|
284
|
+
ws.removeListener('message', onMessage);
|
|
285
|
+
try {
|
|
286
|
+
const msg = JSON.parse(data.toString());
|
|
287
|
+
if (msg.type === 'auth_ok') {
|
|
288
|
+
this.options.towerId = msg.towerId;
|
|
289
|
+
this.startH2Server(ws);
|
|
290
|
+
}
|
|
291
|
+
else if (msg.type === 'auth_error') {
|
|
292
|
+
this.handleAuthError(msg.reason || 'unknown');
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
this.handleConnectionError(new Error(`Unexpected auth response type: ${msg.type}`));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
this.handleConnectionError(new Error(`Invalid auth response: ${data.toString()}`));
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
ws.on('message', onMessage);
|
|
303
|
+
}
|
|
304
|
+
handleAuthError(reason) {
|
|
305
|
+
this.cleanup();
|
|
306
|
+
if (reason === 'invalid_api_key') {
|
|
307
|
+
this.setState('auth_failed');
|
|
308
|
+
console.error("Cloud connection failed: API key is invalid or revoked. Run 'af tower register --reauth' to update credentials.");
|
|
309
|
+
// Circuit breaker: don't retry
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
// Transient errors: rate_limited, internal_error, etc.
|
|
313
|
+
this.setState('disconnected');
|
|
314
|
+
if (reason === 'rate_limited') {
|
|
315
|
+
this.rateLimitCount++;
|
|
316
|
+
// First rate limit: 60s. Subsequent: 5 minutes (per spec).
|
|
317
|
+
const delay = this.rateLimitCount <= 1 ? 60_000 : 300_000;
|
|
318
|
+
this.reconnectTimer = setTimeout(() => {
|
|
319
|
+
this.reconnectTimer = null;
|
|
320
|
+
if (!this.destroyed)
|
|
321
|
+
this.doConnect();
|
|
322
|
+
}, delay);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
this.scheduleReconnect();
|
|
326
|
+
}
|
|
327
|
+
this.consecutiveFailures++;
|
|
328
|
+
}
|
|
329
|
+
handleConnectionError(_err) {
|
|
330
|
+
this.cleanup();
|
|
331
|
+
if (this.state === 'auth_failed')
|
|
332
|
+
return; // Don't override circuit breaker
|
|
333
|
+
this.setState('disconnected');
|
|
334
|
+
this.scheduleReconnect();
|
|
335
|
+
this.consecutiveFailures++;
|
|
336
|
+
}
|
|
337
|
+
startH2Server(ws) {
|
|
338
|
+
// Convert WebSocket to a Node.js duplex stream
|
|
339
|
+
const wsStream = createWebSocketStream(ws);
|
|
340
|
+
this.wsStream = wsStream;
|
|
341
|
+
// Create an HTTP/2 server (plaintext — TLS is handled by the WebSocket layer)
|
|
342
|
+
// Enable extended CONNECT for WebSocket proxying (RFC 8441)
|
|
343
|
+
const h2Server = http2.createServer({
|
|
344
|
+
settings: { enableConnectProtocol: true },
|
|
345
|
+
});
|
|
346
|
+
this.h2Server = h2Server;
|
|
347
|
+
h2Server.on('session', (session) => {
|
|
348
|
+
this.h2Session = session;
|
|
349
|
+
this.setState('connected');
|
|
350
|
+
});
|
|
351
|
+
h2Server.on('stream', (stream, headers) => {
|
|
352
|
+
this.handleH2Stream(stream, headers);
|
|
353
|
+
});
|
|
354
|
+
h2Server.on('error', () => {
|
|
355
|
+
// H2 server error — will be handled by ws close
|
|
356
|
+
});
|
|
357
|
+
// Emit the duplex stream as a connection to the H2 server
|
|
358
|
+
// This is the "role reversal" — the H2 server runs over an outbound WebSocket
|
|
359
|
+
h2Server.emit('connection', wsStream);
|
|
360
|
+
}
|
|
361
|
+
handleH2Stream(stream, headers) {
|
|
362
|
+
const method = headers[':method'];
|
|
363
|
+
const path = headers[':path'];
|
|
364
|
+
const protocol = headers[':protocol'];
|
|
365
|
+
// Check blocklist
|
|
366
|
+
if (path && isBlockedPath(path)) {
|
|
367
|
+
stream.respond({
|
|
368
|
+
':status': 403,
|
|
369
|
+
'content-type': 'application/json',
|
|
370
|
+
});
|
|
371
|
+
stream.end(JSON.stringify({ error: 'Forbidden: tunnel management endpoints are local-only' }));
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
// Handle metadata requests from the server
|
|
375
|
+
if (method === 'GET' && path === '/__tower/metadata') {
|
|
376
|
+
stream.respond({
|
|
377
|
+
':status': 200,
|
|
378
|
+
'content-type': 'application/json',
|
|
379
|
+
});
|
|
380
|
+
stream.end(JSON.stringify(this._pendingMetadata ?? { projects: [], terminals: [] }));
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
// Handle WebSocket CONNECT (RFC 8441)
|
|
384
|
+
if (method === 'CONNECT' && protocol === 'websocket') {
|
|
385
|
+
this.handleWebSocketConnect(stream, headers);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
// Regular HTTP proxy
|
|
389
|
+
this.proxyHttpRequest(stream, headers, method, path);
|
|
390
|
+
}
|
|
391
|
+
handleWebSocketConnect(stream, headers) {
|
|
392
|
+
const authority = headers[':authority'] || `localhost:${this.options.localPort}`;
|
|
393
|
+
const path = headers[':path'] || '/';
|
|
394
|
+
// Forward non-hop-by-hop headers from the H2 CONNECT to the local WS upgrade
|
|
395
|
+
const forwardHeaders = {
|
|
396
|
+
'Upgrade': 'websocket',
|
|
397
|
+
'Connection': 'Upgrade',
|
|
398
|
+
'Sec-WebSocket-Version': '13',
|
|
399
|
+
'Sec-WebSocket-Key': Buffer.from(Math.random().toString()).toString('base64'),
|
|
400
|
+
'Host': authority,
|
|
401
|
+
};
|
|
402
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
403
|
+
if (key.startsWith(':'))
|
|
404
|
+
continue;
|
|
405
|
+
if (HOP_BY_HOP_HEADERS.has(key.toLowerCase()))
|
|
406
|
+
continue;
|
|
407
|
+
if (key.toLowerCase() === 'host')
|
|
408
|
+
continue; // Already set
|
|
409
|
+
if (value !== undefined) {
|
|
410
|
+
forwardHeaders[key] = value;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
// Make HTTP/1.1 WebSocket upgrade request to localhost
|
|
414
|
+
const wsReq = http.request({
|
|
415
|
+
hostname: 'localhost',
|
|
416
|
+
port: this.options.localPort,
|
|
417
|
+
path,
|
|
418
|
+
method: 'GET',
|
|
419
|
+
headers: forwardHeaders,
|
|
420
|
+
});
|
|
421
|
+
wsReq.on('upgrade', (_res, socket, head) => {
|
|
422
|
+
// Respond 200 to the H2 CONNECT
|
|
423
|
+
stream.respond({ ':status': 200 });
|
|
424
|
+
// If there's buffered data from upgrade, push it
|
|
425
|
+
if (head.length > 0) {
|
|
426
|
+
stream.write(head);
|
|
427
|
+
}
|
|
428
|
+
// Bidirectional pipe
|
|
429
|
+
socket.pipe(stream);
|
|
430
|
+
stream.pipe(socket);
|
|
431
|
+
socket.on('error', () => stream.destroy());
|
|
432
|
+
stream.on('error', () => socket.destroy());
|
|
433
|
+
socket.on('close', () => { if (!stream.destroyed)
|
|
434
|
+
stream.destroy(); });
|
|
435
|
+
stream.on('close', () => { if (!socket.destroyed)
|
|
436
|
+
socket.destroy(); });
|
|
437
|
+
});
|
|
438
|
+
// Handle non-upgrade responses (e.g. 404 for missing terminal)
|
|
439
|
+
wsReq.on('response', (res) => {
|
|
440
|
+
if (!stream.destroyed) {
|
|
441
|
+
stream.respond({ ':status': res.statusCode || 502 });
|
|
442
|
+
res.pipe(stream);
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
wsReq.on('error', () => {
|
|
446
|
+
if (!stream.destroyed) {
|
|
447
|
+
stream.respond({ ':status': 502 });
|
|
448
|
+
stream.end();
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
wsReq.end();
|
|
452
|
+
}
|
|
453
|
+
proxyHttpRequest(stream, h2Headers, method, path) {
|
|
454
|
+
// Build HTTP/1.1 request headers, filtering H2 pseudo-headers and hop-by-hop
|
|
455
|
+
const reqHeaders = {};
|
|
456
|
+
for (const [key, value] of Object.entries(h2Headers)) {
|
|
457
|
+
if (key.startsWith(':'))
|
|
458
|
+
continue; // Skip H2 pseudo-headers
|
|
459
|
+
if (HOP_BY_HOP_HEADERS.has(key.toLowerCase()))
|
|
460
|
+
continue;
|
|
461
|
+
if (value !== undefined) {
|
|
462
|
+
reqHeaders[key] = value;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
const proxyReq = http.request({
|
|
466
|
+
hostname: 'localhost',
|
|
467
|
+
port: this.options.localPort,
|
|
468
|
+
path,
|
|
469
|
+
method,
|
|
470
|
+
headers: reqHeaders,
|
|
471
|
+
}, (proxyRes) => {
|
|
472
|
+
// Filter hop-by-hop headers from response
|
|
473
|
+
const responseHeaders = {
|
|
474
|
+
':status': proxyRes.statusCode ?? 500,
|
|
475
|
+
};
|
|
476
|
+
for (const [key, value] of Object.entries(proxyRes.headers)) {
|
|
477
|
+
if (!HOP_BY_HOP_HEADERS.has(key.toLowerCase()) && value !== undefined) {
|
|
478
|
+
responseHeaders[key] = value;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
stream.respond(responseHeaders);
|
|
482
|
+
proxyRes.pipe(stream);
|
|
483
|
+
proxyRes.on('error', () => {
|
|
484
|
+
if (!stream.destroyed)
|
|
485
|
+
stream.destroy();
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
proxyReq.on('error', () => {
|
|
489
|
+
if (!stream.destroyed) {
|
|
490
|
+
stream.respond({ ':status': 502 });
|
|
491
|
+
stream.end(JSON.stringify({ error: 'Bad Gateway: local server unavailable' }));
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
// Pipe request body
|
|
495
|
+
stream.pipe(proxyReq);
|
|
496
|
+
stream.on('error', () => {
|
|
497
|
+
if (!proxyReq.destroyed)
|
|
498
|
+
proxyReq.destroy();
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
//# sourceMappingURL=tunnel-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-client.js","sourceRoot":"","sources":["../../../src/agent-farm/lib/tunnel-client.ts"],"names":[],"mappings":"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"}
|