pi-harness-runtime 1.1.375 → 1.1.377

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.
Files changed (129) hide show
  1. package/README.md +68 -39
  2. package/package.json +2 -1
  3. package/packages/a2a-adapter/package.json +1 -1
  4. package/packages/agent-policy/package.json +1 -1
  5. package/packages/architecture-generator/package.json +1 -1
  6. package/packages/auth/package.json +1 -1
  7. package/packages/autonomous-refactor/package.json +1 -1
  8. package/packages/autonomous-runtime/package.json +1 -1
  9. package/packages/cache-strategy/package.json +1 -1
  10. package/packages/capability-registry/package.json +1 -1
  11. package/packages/checkpoint/package.json +1 -1
  12. package/packages/cli-plugin-sdk/package.json +1 -1
  13. package/packages/clipboard/package.json +1 -1
  14. package/packages/clipboard-plugin/package.json +1 -1
  15. package/packages/code-generation/package.json +1 -1
  16. package/packages/code-review/package.json +1 -1
  17. package/packages/codex-adapter/package.json +1 -1
  18. package/packages/codex-watcher/package.json +1 -1
  19. package/packages/config-capture/package.json +1 -1
  20. package/packages/connection-runtime/package.json +1 -1
  21. package/packages/context-compiler/package.json +1 -1
  22. package/packages/context-discovery/package.json +1 -1
  23. package/packages/context-manager/package.json +1 -1
  24. package/packages/contracts/package.json +1 -1
  25. package/packages/cookie-sanitizer/package.json +1 -1
  26. package/packages/cost-optimizer/package.json +1 -1
  27. package/packages/dependency-analyzer/package.json +1 -1
  28. package/packages/django-plugin/package.json +1 -1
  29. package/packages/doc-generator/package.json +1 -1
  30. package/packages/durable-review/package.json +1 -1
  31. package/packages/e2e-tester/package.json +1 -1
  32. package/packages/evaluation-engine/package.json +1 -1
  33. package/packages/evaluation-runner/package.json +1 -1
  34. package/packages/event-bus/package.json +1 -1
  35. package/packages/event-sink/package.json +1 -1
  36. package/packages/event-store/package.json +1 -1
  37. package/packages/experience-replay/package.json +1 -1
  38. package/packages/feedback-collector/package.json +1 -1
  39. package/packages/file-copy-helper/package.json +1 -1
  40. package/packages/framework-detector/package.json +1 -1
  41. package/packages/framework-plugin-sdk/package.json +1 -1
  42. package/packages/frappe-plugin/package.json +1 -1
  43. package/packages/generic-web-plugin/package.json +1 -1
  44. package/packages/health-monitor/package.json +1 -1
  45. package/packages/intent-analyzer/package.json +1 -1
  46. package/packages/jev-judge/package.json +1 -1
  47. package/packages/knowledge-graph/package.json +1 -1
  48. package/packages/knowledge-retrieval/package.json +1 -1
  49. package/packages/laravel-plugin/package.json +1 -1
  50. package/packages/learning-engine/package.json +1 -1
  51. package/packages/mcp-adapter/package.json +1 -1
  52. package/packages/memory-engine/package.json +1 -1
  53. package/packages/milestone-manager/package.json +1 -1
  54. package/packages/model-registry/package.json +1 -1
  55. package/packages/nextjs-plugin/package.json +1 -1
  56. package/packages/notification/package.json +1 -1
  57. package/packages/observability/package.json +1 -1
  58. package/packages/okf-indexer/package.json +1 -1
  59. package/packages/paperclip-integration/package.json +1 -1
  60. package/packages/performance-optimizer/package.json +1 -1
  61. package/packages/permission-mode/package.json +1 -1
  62. package/packages/privilege-broker/package.json +1 -1
  63. package/packages/project-analyzer/package.json +1 -1
  64. package/packages/project-bootstrap/package.json +1 -1
  65. package/packages/projection-engine/package.json +1 -1
  66. package/packages/prompt-compiler/package.json +1 -1
  67. package/packages/prompt-versioning/package.json +1 -1
  68. package/packages/provider-adapter-sdk/package.json +1 -1
  69. package/packages/provider-router/package.json +1 -1
  70. package/packages/provider-selector/package.json +1 -1
  71. package/packages/providers/package.json +1 -1
  72. package/packages/qdrant-skills/package.json +1 -1
  73. package/packages/quota-manager/package.json +1 -1
  74. package/packages/rate-limiter/package.json +1 -1
  75. package/packages/react-vite-plugin/package.json +1 -1
  76. package/packages/relay/dist/client.d.ts +56 -35
  77. package/packages/relay/dist/client.js +278 -71
  78. package/packages/relay/dist/index.d.ts +12 -5
  79. package/packages/relay/dist/index.js +12 -4
  80. package/packages/relay/dist/server.d.ts +58 -20
  81. package/packages/relay/dist/server.js +344 -49
  82. package/packages/relay/dist/types.d.ts +137 -1
  83. package/packages/relay/dist/types.js +21 -2
  84. package/packages/relay/dist/ws-server.d.ts +48 -0
  85. package/packages/relay/dist/ws-server.js +130 -0
  86. package/packages/relay/package.json +1 -1
  87. package/packages/relay/src/client.ts +375 -81
  88. package/packages/relay/src/index.ts +42 -5
  89. package/packages/relay/src/server.ts +452 -54
  90. package/packages/relay/src/types.ts +174 -1
  91. package/packages/relay/src/ws-server.ts +165 -0
  92. package/packages/release-manager/package.json +1 -1
  93. package/packages/requirement-compiler/package.json +1 -1
  94. package/packages/runtime/package.json +1 -1
  95. package/packages/scheduler/package.json +1 -1
  96. package/packages/scheduler-adapter/package.json +1 -1
  97. package/packages/session/package.json +1 -1
  98. package/packages/session-api/package.json +1 -1
  99. package/packages/session-export/package.json +1 -1
  100. package/packages/shared-context/package.json +1 -1
  101. package/packages/skill-mcp-client/package.json +1 -1
  102. package/packages/skill-registry/package.json +1 -1
  103. package/packages/skills/package.json +1 -1
  104. package/packages/sprint-planner/package.json +1 -1
  105. package/packages/subscription-engine/package.json +1 -1
  106. package/packages/task-compiler/package.json +1 -1
  107. package/packages/telegram/package.json +1 -1
  108. package/packages/tencentdb-memory/package.json +1 -1
  109. package/packages/tencentdb-sync/package.json +1 -1
  110. package/packages/test-data-generator/package.json +1 -1
  111. package/packages/test-generator/package.json +1 -1
  112. package/packages/todo-bd-sync/package.json +1 -1
  113. package/packages/token-estimation/package.json +1 -1
  114. package/packages/token-optimizer/package.json +1 -1
  115. package/packages/trajectory/package.json +1 -1
  116. package/packages/tui/package.json +1 -1
  117. package/packages/types/package.json +1 -1
  118. package/packages/voice-api/package.json +1 -1
  119. package/packages/webmcp-tester/package.json +1 -1
  120. package/packages/wiki/package.json +1 -1
  121. package/packages/workflow-events/package.json +1 -1
  122. package/packages/workspace-scanner/package.json +1 -1
  123. package/packages/worktree/package.json +1 -1
  124. package/packages/write-review/package.json +1 -1
  125. package/scripts/relay-client.ts +184 -0
  126. package/scripts/sync-callback-queue.ts +57 -0
  127. package/scripts/telegram-callback-processor.ts +16 -0
  128. package/scripts/unified-webhook-server.ts +160 -145
  129. package/scripts/ws-server.ts +81 -0
@@ -1,95 +1,302 @@
1
1
  /**
2
- * Relay Client - Connect to relay server for remote access
3
- */
4
- /**
5
- * Generate a UUID v4
6
- */
7
- function generateUUID() {
8
- // Use crypto.randomUUID if available (Node.js 14.17+)
9
- const crypto = globalThis;
10
- if (crypto.crypto?.randomUUID) {
11
- return crypto.crypto.randomUUID();
12
- }
13
- // Fallback: simple UUID generation
14
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
15
- const r = (Math.random() * 16) | 0;
16
- const v = c === 'x' ? r : (r & 0x3) | 0x8;
17
- return v.toString(16);
18
- });
19
- }
20
- /**
21
- * Relay Client
2
+ * Relay Client - Two-way streaming WebSocket client
22
3
  *
23
- * Connects to a relay server and establishes tunnels for remote access.
4
+ * Implements t3code-style streaming with:
5
+ * - Connection state machine
6
+ * - Snapshot + live event handling
7
+ * - Typed message protocol
8
+ * - Automatic reconnection with backoff
24
9
  */
10
+ import WebSocket from "ws";
11
+ import { PROTOCOL_VERSION, createTimestamp, } from './types.js';
12
+ // ============================================================================
13
+ // Constants
14
+ // ============================================================================
15
+ const DEFAULT_RECONNECT_DELAY_MS = 1000;
16
+ const MAX_RECONNECT_DELAY_MS = 30000;
17
+ const HEARTBEAT_INTERVAL_MS = 30000;
18
+ const CONNECT_TIMEOUT_MS = 10000;
19
+ // ============================================================================
20
+ // Relay Client - Two-way Streaming Implementation
21
+ // ============================================================================
25
22
  export class RelayClient {
23
+ ws = null;
26
24
  config;
27
- options;
28
- connected = false;
29
- tunnel;
30
- constructor(config, options) {
25
+ callbacks;
26
+ // State machine
27
+ state = { state: 'disconnected' };
28
+ sessionId = null;
29
+ clientId = null;
30
+ // Reconnection
31
+ reconnectAttempt = 0;
32
+ reconnectDelay = DEFAULT_RECONNECT_DELAY_MS;
33
+ reconnectTimer = null;
34
+ connectTimeoutTimer = null;
35
+ // Heartbeat
36
+ heartbeatTimer = null;
37
+ // Snapshot tracking (t3code pattern)
38
+ snapshotReceived = false;
39
+ synchronizedReceived = false;
40
+ constructor(config, callbacks = {}) {
31
41
  this.config = config;
32
- this.options = options;
42
+ this.callbacks = callbacks;
43
+ }
44
+ // ========================================================================
45
+ // Connection State Machine
46
+ // ========================================================================
47
+ setState(newState) {
48
+ const previous = this.state;
49
+ this.state = newState;
50
+ this.callbacks.onStateChange?.({
51
+ previous,
52
+ current: newState,
53
+ timestamp: createTimestamp(),
54
+ });
33
55
  }
56
+ isConnected() {
57
+ return this.ws?.readyState === WebSocket.OPEN &&
58
+ this.state.state === 'connected';
59
+ }
60
+ // ========================================================================
61
+ // Lifecycle
62
+ // ========================================================================
34
63
  /**
35
- * Connect to relay server
64
+ * Connect to relay server with automatic authentication
36
65
  */
37
66
  async connect() {
38
- if (this.connected)
67
+ if (this.isConnected())
39
68
  return;
40
- try {
41
- // In a real implementation, this would establish WebSocket connection
42
- // to the relay server and authenticate
43
- this.connected = true;
44
- this.options.onConnected?.();
45
- }
46
- catch (error) {
47
- this.options.onError?.(error);
48
- throw error;
49
- }
69
+ // Clear any existing connection
70
+ this.cleanup();
71
+ this.setState({ state: 'connecting' });
72
+ const url = this.config.serverUrl;
73
+ console.log(`[RelayClient] Connecting to ${url}...`);
74
+ return new Promise((resolve, reject) => {
75
+ // Set connect timeout
76
+ this.connectTimeoutTimer = setTimeout(() => {
77
+ if (this.state.state === 'connecting') {
78
+ this.ws?.close();
79
+ reject(new Error('Connection timeout'));
80
+ }
81
+ }, CONNECT_TIMEOUT_MS);
82
+ this.ws = new WebSocket(url);
83
+ this.ws.on("open", () => {
84
+ console.log("[RelayClient] WebSocket opened");
85
+ this.setState({ state: 'authenticating' });
86
+ // Send authentication
87
+ this.send({
88
+ version: PROTOCOL_VERSION,
89
+ type: 'auth',
90
+ payload: { token: this.config.token || '' },
91
+ timestamp: createTimestamp(),
92
+ });
93
+ });
94
+ this.ws.on("message", (data) => {
95
+ this.handleMessage(data.toString());
96
+ });
97
+ this.ws.on("close", () => {
98
+ console.log("[RelayClient] WebSocket closed");
99
+ this.handleDisconnect();
100
+ });
101
+ this.ws.on("error", (err) => {
102
+ console.error("[RelayClient] WebSocket error:", err.message);
103
+ this.callbacks.onError?.(err);
104
+ if (this.state.state === 'connecting' || this.state.state === 'authenticating') {
105
+ clearTimeout(this.connectTimeoutTimer);
106
+ reject(err);
107
+ }
108
+ this.handleDisconnect();
109
+ });
110
+ });
50
111
  }
51
112
  /**
52
113
  * Disconnect from relay server
53
114
  */
54
- async disconnect() {
55
- this.connected = false;
56
- this.tunnel = undefined;
57
- this.options.onDisconnected?.();
115
+ disconnect() {
116
+ console.log("[RelayClient] Disconnecting...");
117
+ this.cleanup();
118
+ this.setState({ state: 'disconnected' });
119
+ this.callbacks.onDisconnected?.();
58
120
  }
59
121
  /**
60
- * Create a tunnel
122
+ * Send a message through the WebSocket
61
123
  */
62
- async createTunnel(localPort) {
63
- const tunnel = {
64
- id: generateUUID(),
65
- url: `${this.config.serverUrl}/tunnel/${localPort}`,
66
- localPort,
67
- status: this.connected ? 'connected' : 'disconnected',
68
- createdAt: new Date().toISOString(),
69
- };
70
- this.tunnel = tunnel;
71
- this.options.onTunnel?.(tunnel);
72
- return tunnel;
124
+ send(message) {
125
+ if (this.isConnected()) {
126
+ this.ws.send(JSON.stringify(message));
127
+ }
73
128
  }
74
- /**
75
- * Send a message through the tunnel
76
- */
77
- async send(_message) {
78
- if (!this.connected) {
79
- throw new Error('Not connected to relay server');
129
+ // ========================================================================
130
+ // Message Handling
131
+ // ========================================================================
132
+ handleMessage(data) {
133
+ try {
134
+ const msg = JSON.parse(data);
135
+ switch (msg.type) {
136
+ case 'connected':
137
+ this.handleConnected(msg);
138
+ break;
139
+ case 'auth':
140
+ this.handleAuth(msg);
141
+ break;
142
+ case 'snapshot':
143
+ this.handleSnapshot(msg);
144
+ break;
145
+ case 'synchronized':
146
+ this.handleSynchronized(msg);
147
+ break;
148
+ case 'callback':
149
+ this.handleCallback(msg);
150
+ break;
151
+ case 'status':
152
+ this.handleStatus(msg);
153
+ break;
154
+ case 'pong':
155
+ // Heartbeat response - connection is alive
156
+ break;
157
+ default: {
158
+ const unknownMsg = msg;
159
+ console.warn(`[RelayClient] Unknown message type: ${unknownMsg.type}`);
160
+ }
161
+ }
162
+ }
163
+ catch (err) {
164
+ console.error("[RelayClient] Failed to parse message:", err);
165
+ this.callbacks.onError?.(err);
80
166
  }
81
- // In a real implementation, this would send the message over WebSocket
82
167
  }
83
- /**
84
- * Check if connected
85
- */
86
- isConnected() {
87
- return this.connected;
168
+ handleConnected(msg) {
169
+ this.clientId = msg.payload.clientId;
170
+ console.log(`[RelayClient] Connected, clientId: ${this.clientId}`);
88
171
  }
89
- /**
90
- * Get current tunnel
91
- */
92
- getTunnel() {
93
- return this.tunnel;
172
+ handleAuth(msg) {
173
+ if (msg.payload.success) {
174
+ this.sessionId = msg.payload.sessionId || null;
175
+ this.setState({ state: 'connected', sessionId: this.sessionId || '' });
176
+ // Reset reconnection state
177
+ this.reconnectAttempt = 0;
178
+ this.reconnectDelay = DEFAULT_RECONNECT_DELAY_MS;
179
+ // Start heartbeat
180
+ this.startHeartbeat();
181
+ // Notify callback
182
+ this.callbacks.onConnected?.(this.sessionId || '');
183
+ console.log(`[RelayClient] Authenticated, sessionId: ${this.sessionId}`);
184
+ }
185
+ else {
186
+ console.error(`[RelayClient] Auth failed: ${msg.payload.error}`);
187
+ this.callbacks.onError?.(new Error(msg.payload.error || 'Authentication failed'));
188
+ this.disconnect();
189
+ }
190
+ }
191
+ handleSnapshot(msg) {
192
+ console.log(`[RelayClient] Snapshot received: ${msg.payload.recentCallbacks.length} recent callbacks`);
193
+ this.snapshotReceived = true;
194
+ // Notify callback
195
+ this.callbacks.onSnapshot?.(msg.payload);
196
+ }
197
+ handleSynchronized(_msg) {
198
+ console.log("[RelayClient] Synchronized - live events starting");
199
+ this.synchronizedReceived = true;
200
+ // Notify callback
201
+ this.callbacks.onSynchronized?.();
202
+ }
203
+ handleCallback(msg) {
204
+ // Only process callbacks after synchronized
205
+ if (!this.synchronizedReceived) {
206
+ console.warn("[RelayClient] Ignoring callback before synchronized");
207
+ return;
208
+ }
209
+ this.callbacks.onCallback?.(msg.payload);
210
+ }
211
+ handleStatus(msg) {
212
+ console.log(`[RelayClient] Status [${msg.payload.kind}]: ${msg.payload.code} - ${msg.payload.message}`);
213
+ this.callbacks.onStatus?.(msg.payload);
214
+ }
215
+ // ========================================================================
216
+ // Disconnect & Reconnect
217
+ // ========================================================================
218
+ handleDisconnect() {
219
+ this.stopHeartbeat();
220
+ const wasConnected = this.state.state === 'connected';
221
+ this.setState({ state: 'disconnected' });
222
+ if (wasConnected) {
223
+ this.callbacks.onDisconnected?.();
224
+ this.scheduleReconnect();
225
+ }
226
+ }
227
+ scheduleReconnect() {
228
+ if (this.reconnectTimer)
229
+ return;
230
+ this.reconnectAttempt++;
231
+ this.setState({ state: 'reconnecting', attempt: this.reconnectAttempt });
232
+ console.log(`[RelayClient] Scheduling reconnect attempt ${this.reconnectAttempt} in ${this.reconnectDelay}ms`);
233
+ this.reconnectTimer = setTimeout(() => {
234
+ this.reconnectTimer = null;
235
+ this.connect().catch((err) => {
236
+ console.error(`[RelayClient] Reconnect failed: ${err.message}`);
237
+ });
238
+ }, this.reconnectDelay);
239
+ // Exponential backoff
240
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, MAX_RECONNECT_DELAY_MS);
241
+ }
242
+ // ========================================================================
243
+ // Heartbeat
244
+ // ========================================================================
245
+ startHeartbeat() {
246
+ this.stopHeartbeat();
247
+ this.heartbeatTimer = setInterval(() => {
248
+ if (this.isConnected()) {
249
+ this.send({
250
+ version: PROTOCOL_VERSION,
251
+ type: 'ping',
252
+ payload: {},
253
+ timestamp: createTimestamp(),
254
+ });
255
+ }
256
+ }, HEARTBEAT_INTERVAL_MS);
257
+ }
258
+ stopHeartbeat() {
259
+ if (this.heartbeatTimer) {
260
+ clearInterval(this.heartbeatTimer);
261
+ this.heartbeatTimer = null;
262
+ }
263
+ }
264
+ // ========================================================================
265
+ // Cleanup
266
+ // ========================================================================
267
+ cleanup() {
268
+ // Clear timers
269
+ if (this.reconnectTimer) {
270
+ clearTimeout(this.reconnectTimer);
271
+ this.reconnectTimer = null;
272
+ }
273
+ if (this.connectTimeoutTimer) {
274
+ clearTimeout(this.connectTimeoutTimer);
275
+ this.connectTimeoutTimer = null;
276
+ }
277
+ this.stopHeartbeat();
278
+ // Reset snapshot state
279
+ this.snapshotReceived = false;
280
+ this.synchronizedReceived = false;
281
+ // Close WebSocket
282
+ if (this.ws) {
283
+ this.ws.close();
284
+ this.ws = null;
285
+ }
286
+ }
287
+ // ========================================================================
288
+ // Public API
289
+ // ========================================================================
290
+ getState() {
291
+ return this.state;
292
+ }
293
+ isReady() {
294
+ return this.isConnected() && this.synchronizedReceived;
295
+ }
296
+ getSessionId() {
297
+ return this.sessionId;
298
+ }
299
+ getClientId() {
300
+ return this.clientId;
94
301
  }
95
302
  }
@@ -1,9 +1,16 @@
1
1
  /**
2
- * Cloud Relay - T3 Connect Style
2
+ * Cloud Relay - Two-way Streaming Communication
3
3
  *
4
- * Enables remote access to pi-harness without router/port forwarding.
5
- * Based on T3Code's T3 Connect architecture.
4
+ * Enables real-time two-way communication with pi-harness without router/port forwarding.
5
+ * Based on T3Code's streaming architecture:
6
+ * - Snapshot + Live event pattern
7
+ * - Typed message protocol
8
+ * - Connection state machine
9
+ * - Stream coalescing
6
10
  */
7
- export { RelayServer } from './server.js';
11
+ export { RelayServer, createRelayServer } from './server.js';
12
+ export type { RelayServerOptions } from './server.js';
8
13
  export { RelayClient } from './client.js';
9
- export { RelayConfig, RelayTunnel } from './types.js';
14
+ export type { RelayClientCallbacks } from './client.js';
15
+ export { PROTOCOL_VERSION, generateMessageId, createTimestamp, } from './types.js';
16
+ export type { RelayConfig, RelayTunnel, ConnectionState, ConnectionStateChange, BaseMessage, SnapshotMessage, SynchronizedMessage, CallbackPayload, CallbackMessage, StatusKind, StatusMessage, PingMessage, PongMessage, AuthRequestMessage, AuthResponseMessage, SubscribeMessage, ConnectedMessage, ServerMessage, ClientMessage, RelayMessage, } from './types.js';
@@ -1,8 +1,16 @@
1
1
  /**
2
- * Cloud Relay - T3 Connect Style
2
+ * Cloud Relay - Two-way Streaming Communication
3
3
  *
4
- * Enables remote access to pi-harness without router/port forwarding.
5
- * Based on T3Code's T3 Connect architecture.
4
+ * Enables real-time two-way communication with pi-harness without router/port forwarding.
5
+ * Based on T3Code's streaming architecture:
6
+ * - Snapshot + Live event pattern
7
+ * - Typed message protocol
8
+ * - Connection state machine
9
+ * - Stream coalescing
6
10
  */
7
- export { RelayServer } from './server.js';
11
+ // Server
12
+ export { RelayServer, createRelayServer } from './server.js';
13
+ // Client
8
14
  export { RelayClient } from './client.js';
15
+ // Types
16
+ export { PROTOCOL_VERSION, generateMessageId, createTimestamp, } from './types.js';
@@ -1,58 +1,96 @@
1
1
  /**
2
- * Relay Server - Cloud relay server for tunnels
3
- */
4
- import type { RelayTunnel, RelayMessage } from './types.js';
5
- /**
6
- * Relay Server options
2
+ * Relay Server - Two-way streaming WebSocket relay
3
+ *
4
+ * Implements t3code-style streaming with:
5
+ * - Snapshot + Live event pattern
6
+ * - Typed message protocol
7
+ * - Connection state machine
8
+ * - Stream coalescing
7
9
  */
10
+ import { type RelayTunnel, type RelayMessage, type ConnectionState, type ConnectionStateChange, type CallbackPayload } from './types.js';
8
11
  export interface RelayServerOptions {
9
12
  /** Port to listen on */
10
13
  port?: number;
11
14
  /** Host to bind */
12
15
  host?: string;
16
+ /** Authentication token */
17
+ authToken?: string;
18
+ /** Server version string */
19
+ serverVersion?: string;
13
20
  /** Called when a tunnel connects */
14
21
  onTunnelConnect?: (tunnel: RelayTunnel) => void;
15
22
  /** Called when a tunnel disconnects */
16
23
  onTunnelDisconnect?: (tunnelId: string) => void;
17
24
  /** Called when a message is received */
18
25
  onMessage?: (tunnelId: string, message: RelayMessage) => void;
26
+ /** Called on state change */
27
+ onStateChange?: (change: ConnectionStateChange) => void;
19
28
  }
20
- /**
21
- * Relay Server
22
- *
23
- * Manages tunnels and relays messages between clients.
24
- */
25
29
  export declare class RelayServer {
30
+ private server;
31
+ private wss;
32
+ private clients;
26
33
  private tunnels;
27
34
  private options;
28
35
  private running;
36
+ private serverSessionId;
37
+ private eventBuffer;
38
+ private recentCallbacks;
29
39
  constructor(options?: RelayServerOptions);
30
40
  /**
31
- * Start the relay server
41
+ * Start the WebSocket server
32
42
  */
33
43
  start(): Promise<void>;
34
44
  /**
35
45
  * Stop the relay server
36
46
  */
37
47
  stop(): Promise<void>;
48
+ private updateState;
49
+ private handleMessage;
50
+ private handleAuth;
51
+ private handlePing;
52
+ private handleSubscribe;
53
+ /**
54
+ * Send initial snapshot followed by synchronized marker
55
+ * This is the t3code pattern: snapshot -> synchronized -> live events
56
+ */
57
+ private sendSnapshot;
58
+ /**
59
+ * Flush event buffer and send batched callbacks
60
+ */
61
+ private flushEventBuffer;
62
+ /**
63
+ * Broadcast callback to all connected clients
64
+ * Uses stream batching for high-frequency events
65
+ */
66
+ broadcastCallback(callback: CallbackPayload, batch?: boolean): void;
38
67
  /**
39
- * Get all active tunnels
68
+ * Send status to all clients
40
69
  */
41
- getTunnels(): RelayTunnel[];
70
+ broadcastStatus(kind: 'info' | 'warning' | 'error', code: string, message: string): void;
42
71
  /**
43
- * Get tunnel by ID
72
+ * Broadcast to all subscribed clients
44
73
  */
45
- getTunnel(id: string): RelayTunnel | undefined;
74
+ private broadcast;
46
75
  /**
47
- * Create a tunnel
76
+ * Check if client is connected and authenticated
48
77
  */
49
- createTunnel(localPort: number): RelayTunnel;
78
+ private isConnected;
50
79
  /**
51
- * Remove a tunnel
80
+ * Send a typed message to a client
52
81
  */
53
- removeTunnel(id: string): boolean;
82
+ private sendMessage;
54
83
  /**
55
- * Check if server is running
84
+ * Send status to specific client
56
85
  */
86
+ private sendStatus;
57
87
  isRunning(): boolean;
88
+ getClientCount(): number;
89
+ getClients(): Array<{
90
+ id: string;
91
+ state: ConnectionState;
92
+ connectedAt: string;
93
+ }>;
94
+ getRecentCallbacks(limit?: number): CallbackPayload[];
58
95
  }
96
+ export declare function createRelayServer(options?: RelayServerOptions): RelayServer;